| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/policy/policy_loader_win.h" | 5 #include "chrome/browser/policy/policy_loader_win.h" |
| 6 | 6 |
| 7 #include <userenv.h> | 7 #include <userenv.h> |
| 8 #include <windows.h> | 8 #include <windows.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 #include "base/strings/string_number_conversions.h" | 25 #include "base/strings/string_number_conversions.h" |
| 26 #include "base/strings/string_util.h" | 26 #include "base/strings/string_util.h" |
| 27 #include "base/strings/stringprintf.h" | 27 #include "base/strings/stringprintf.h" |
| 28 #include "base/strings/utf_string_conversions.h" | 28 #include "base/strings/utf_string_conversions.h" |
| 29 #include "base/sys_byteorder.h" | 29 #include "base/sys_byteorder.h" |
| 30 #include "base/win/registry.h" | 30 #include "base/win/registry.h" |
| 31 #include "chrome/browser/policy/async_policy_provider.h" | 31 #include "chrome/browser/policy/async_policy_provider.h" |
| 32 #include "chrome/browser/policy/configuration_policy_provider_test.h" | 32 #include "chrome/browser/policy/configuration_policy_provider_test.h" |
| 33 #include "chrome/browser/policy/external_data_fetcher.h" | 33 #include "chrome/browser/policy/external_data_fetcher.h" |
| 34 #include "chrome/browser/policy/policy_bundle.h" | 34 #include "chrome/browser/policy/policy_bundle.h" |
| 35 #include "chrome/browser/policy/policy_domain_descriptor.h" |
| 35 #include "chrome/browser/policy/policy_map.h" | 36 #include "chrome/browser/policy/policy_map.h" |
| 36 #include "chrome/browser/policy/preg_parser_win.h" | 37 #include "chrome/browser/policy/preg_parser_win.h" |
| 37 #include "components/json_schema/json_schema_constants.h" | 38 #include "components/json_schema/json_schema_constants.h" |
| 38 #include "policy/policy_constants.h" | 39 #include "policy/policy_constants.h" |
| 39 #include "testing/gtest/include/gtest/gtest.h" | 40 #include "testing/gtest/include/gtest/gtest.h" |
| 40 | 41 |
| 41 namespace schema = json_schema_constants; | 42 namespace schema = json_schema_constants; |
| 42 | 43 |
| 43 using base::win::RegKey; | 44 using base::win::RegKey; |
| 44 | 45 |
| 45 namespace policy { | 46 namespace policy { |
| 46 | 47 |
| 47 namespace { | 48 namespace { |
| 48 | 49 |
| 49 // Constants for registry key names. | 50 // Constants for registry key names. |
| 50 const wchar_t kPathSep[] = L"\\"; | 51 const wchar_t kPathSep[] = L"\\"; |
| 51 const wchar_t kThirdParty[] = L"3rdparty"; | 52 const wchar_t kThirdParty[] = L"3rdparty"; |
| 52 const wchar_t kMandatory[] = L"policy"; | 53 const wchar_t kMandatory[] = L"policy"; |
| 53 const wchar_t kRecommended[] = L"recommended"; | 54 const wchar_t kRecommended[] = L"recommended"; |
| 54 const wchar_t kSchema[] = L"schema"; | 55 const wchar_t kSchema[] = L"schema"; |
| 55 | 56 |
| 57 void InstallTestChromePolicySchema(AsyncPolicyLoader* loader) { |
| 58 std::string err = ""; |
| 59 scoped_ptr<PolicySchema> schema = PolicySchema::Parse( |
| 60 "{" |
| 61 " \"$schema\": \"http://json-schema.org/draft-03/schema#\"," |
| 62 " \"type\": \"object\"," |
| 63 " \"properties\": {" |
| 64 " \"BooleanPolicy\": { \"type\": \"boolean\" }," |
| 65 " \"IntegerPolicy\": { \"type\": \"integer\" }," |
| 66 " \"StringListPolicy\": {" |
| 67 " \"type\": \"array\"," |
| 68 " \"items\": { \"type\": \"string\" } " |
| 69 " }," |
| 70 " \"StringPolicy\": { \"type\": \"string\" }," |
| 71 " \"DictionaryPolicy\": {" |
| 72 " \"type\": \"object\", " |
| 73 " \"properties\": {" |
| 74 " \"int\": {" |
| 75 " \"type\": \"integer\"" |
| 76 " }, " |
| 77 " \"list\": {" |
| 78 " \"items\": {" |
| 79 " \"type\": \"string\"" |
| 80 " }, " |
| 81 " \"type\": \"array\"" |
| 82 " }, " |
| 83 " \"bool\": {" |
| 84 " \"type\": \"boolean\"" |
| 85 " }, " |
| 86 " \"str\": {" |
| 87 " \"type\": \"string\"" |
| 88 " }, " |
| 89 " \"dict\": {" |
| 90 " \"type\": \"object\", " |
| 91 " \"properties\": {" |
| 92 " \"sub\": {" |
| 93 " \"type\": \"string\"" |
| 94 " }, " |
| 95 " \"sublist\": {" |
| 96 " \"items\": {" |
| 97 " \"type\": \"object\", " |
| 98 " \"properties\": {" |
| 99 " \"aaa\": {" |
| 100 " \"type\": \"integer\"" |
| 101 " }, " |
| 102 " \"bbb\": {" |
| 103 " \"type\": \"integer\"" |
| 104 " }, " |
| 105 " \"ccc\": {" |
| 106 " \"type\": \"string\"" |
| 107 " }, " |
| 108 " \"ddd\": {" |
| 109 " \"type\": \"string\"" |
| 110 " }" |
| 111 " }" |
| 112 " }, " |
| 113 " \"type\": \"array\"" |
| 114 " }" |
| 115 " }" |
| 116 " }" |
| 117 " }" |
| 118 " }" |
| 119 " }" |
| 120 "}", |
| 121 &err); |
| 122 CHECK("" == err) << err; |
| 123 scoped_refptr<PolicyDomainDescriptor> descriptor = |
| 124 new PolicyDomainDescriptor(POLICY_DOMAIN_CHROME); |
| 125 descriptor->RegisterComponent("", schema.Pass()); |
| 126 loader->InitialRegisterPolicyDomain(descriptor); |
| 127 } |
| 128 |
| 56 // Installs |value| in the given registry |path| and |hive|, under the key | 129 // Installs |value| in the given registry |path| and |hive|, under the key |
| 57 // |name|. Returns false on errors. | 130 // |name|. Returns false on errors. |
| 58 // Some of the possible Value types are stored after a conversion (e.g. doubles | 131 // Some of the possible Value types are stored after a conversion (e.g. doubles |
| 59 // are stored as strings), and can only be retrieved if a corresponding schema | 132 // are stored as strings), and can only be retrieved if a corresponding schema |
| 60 // is written. | 133 // is written. |
| 61 bool InstallValue(const base::Value& value, | 134 bool InstallValue(const base::Value& value, |
| 62 HKEY hive, | 135 HKEY hive, |
| 63 const string16& path, | 136 const string16& path, |
| 64 const string16& name) { | 137 const string16& name) { |
| 65 // KEY_ALL_ACCESS causes the ctor to create the key if it does not exist yet. | 138 // KEY_ALL_ACCESS causes the ctor to create the key if it does not exist yet. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 } | 249 } |
| 177 schema->SetString(schema::kType, "object"); | 250 schema->SetString(schema::kType, "object"); |
| 178 schema->Set(schema::kProperties, properties); | 251 schema->Set(schema::kProperties, properties); |
| 179 } | 252 } |
| 180 break; | 253 break; |
| 181 } | 254 } |
| 182 | 255 |
| 183 case base::Value::TYPE_BINARY: | 256 case base::Value::TYPE_BINARY: |
| 184 break; | 257 break; |
| 185 } | 258 } |
| 259 schema->SetString(schema::kSchema, "http://json-schema.org/draft-03/schema#"); |
| 186 return schema; | 260 return schema; |
| 187 } | 261 } |
| 188 | 262 |
| 189 // Writes a JSON |schema| at the registry entry |name| at |path| | 263 // Writes a JSON |schema| at the registry entry |name| at |path| |
| 190 // in the given |hive|. Returns false on failure. | 264 // in the given |hive|. Returns false on failure. |
| 191 bool WriteSchema(const base::DictionaryValue& schema, | 265 bool WriteSchema(const base::DictionaryValue& schema, |
| 192 HKEY hive, | 266 HKEY hive, |
| 193 const string16& path, | 267 const string16& path, |
| 194 const string16& name) { | 268 const string16& name) { |
| 195 std::string encoded; | 269 std::string encoded; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 206 // Builds a JSON schema for |value| and writes it at the registry entry |name| | 280 // Builds a JSON schema for |value| and writes it at the registry entry |name| |
| 207 // at |path| in the given |hive|. Returns false on failure. | 281 // at |path| in the given |hive|. Returns false on failure. |
| 208 bool InstallSchema(const base::Value& value, | 282 bool InstallSchema(const base::Value& value, |
| 209 HKEY hive, | 283 HKEY hive, |
| 210 const string16& path, | 284 const string16& path, |
| 211 const string16& name) { | 285 const string16& name) { |
| 212 scoped_ptr<base::DictionaryValue> schema_dict(BuildSchema(value)); | 286 scoped_ptr<base::DictionaryValue> schema_dict(BuildSchema(value)); |
| 213 return WriteSchema(*schema_dict, hive, path, name); | 287 return WriteSchema(*schema_dict, hive, path, name); |
| 214 } | 288 } |
| 215 | 289 |
| 290 void DoNothing(scoped_ptr<PolicyBundle> bundle) { |
| 291 } |
| 292 |
| 216 // This class provides sandboxing and mocking for the parts of the Windows | 293 // This class provides sandboxing and mocking for the parts of the Windows |
| 217 // Registry implementing Group Policy. It prepares two temporary sandbox keys, | 294 // Registry implementing Group Policy. It prepares two temporary sandbox keys, |
| 218 // one for HKLM and one for HKCU. A test's calls to the registry are redirected | 295 // one for HKLM and one for HKCU. A test's calls to the registry are redirected |
| 219 // by Windows to these sandboxes, allowing the tests to manipulate and access | 296 // by Windows to these sandboxes, allowing the tests to manipulate and access |
| 220 // policy as if it were active, but without actually changing the parts of the | 297 // policy as if it were active, but without actually changing the parts of the |
| 221 // Registry that are managed by Group Policy. | 298 // Registry that are managed by Group Policy. |
| 222 class ScopedGroupPolicyRegistrySandbox { | 299 class ScopedGroupPolicyRegistrySandbox { |
| 223 public: | 300 public: |
| 224 ScopedGroupPolicyRegistrySandbox(); | 301 ScopedGroupPolicyRegistrySandbox(); |
| 225 ~ScopedGroupPolicyRegistrySandbox(); | 302 ~ScopedGroupPolicyRegistrySandbox(); |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 417 | 494 |
| 418 RegistryTestHarness::~RegistryTestHarness() {} | 495 RegistryTestHarness::~RegistryTestHarness() {} |
| 419 | 496 |
| 420 void RegistryTestHarness::SetUp() {} | 497 void RegistryTestHarness::SetUp() {} |
| 421 | 498 |
| 422 ConfigurationPolicyProvider* RegistryTestHarness::CreateProvider( | 499 ConfigurationPolicyProvider* RegistryTestHarness::CreateProvider( |
| 423 scoped_refptr<base::SequencedTaskRunner> task_runner, | 500 scoped_refptr<base::SequencedTaskRunner> task_runner, |
| 424 const PolicyDefinitionList* policy_list) { | 501 const PolicyDefinitionList* policy_list) { |
| 425 scoped_ptr<AsyncPolicyLoader> loader(new PolicyLoaderWin( | 502 scoped_ptr<AsyncPolicyLoader> loader(new PolicyLoaderWin( |
| 426 task_runner, policy_list, kRegistryChromePolicyKey, this)); | 503 task_runner, policy_list, kRegistryChromePolicyKey, this)); |
| 504 InstallTestChromePolicySchema(loader.get()); |
| 427 return new AsyncPolicyProvider(loader.Pass()); | 505 return new AsyncPolicyProvider(loader.Pass()); |
| 428 } | 506 } |
| 429 | 507 |
| 430 void RegistryTestHarness::InstallEmptyPolicy() {} | 508 void RegistryTestHarness::InstallEmptyPolicy() {} |
| 431 | 509 |
| 432 void RegistryTestHarness::InstallStringPolicy( | 510 void RegistryTestHarness::InstallStringPolicy( |
| 433 const std::string& policy_name, | 511 const std::string& policy_name, |
| 434 const std::string& policy_value) { | 512 const std::string& policy_value) { |
| 435 RegKey key(hive_, kRegistryChromePolicyKey, KEY_ALL_ACCESS); | 513 RegKey key(hive_, kRegistryChromePolicyKey, KEY_ALL_ACCESS); |
| 436 ASSERT_TRUE(key.Valid()); | 514 ASSERT_TRUE(key.Valid()); |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 549 | 627 |
| 550 memset(&gpo_, 0, sizeof(GROUP_POLICY_OBJECT)); | 628 memset(&gpo_, 0, sizeof(GROUP_POLICY_OBJECT)); |
| 551 gpo_.lpFileSysPath = const_cast<wchar_t*>(temp_dir_.path().value().c_str()); | 629 gpo_.lpFileSysPath = const_cast<wchar_t*>(temp_dir_.path().value().c_str()); |
| 552 } | 630 } |
| 553 | 631 |
| 554 ConfigurationPolicyProvider* PRegTestHarness::CreateProvider( | 632 ConfigurationPolicyProvider* PRegTestHarness::CreateProvider( |
| 555 scoped_refptr<base::SequencedTaskRunner> task_runner, | 633 scoped_refptr<base::SequencedTaskRunner> task_runner, |
| 556 const PolicyDefinitionList* policy_list) { | 634 const PolicyDefinitionList* policy_list) { |
| 557 scoped_ptr<AsyncPolicyLoader> loader(new PolicyLoaderWin( | 635 scoped_ptr<AsyncPolicyLoader> loader(new PolicyLoaderWin( |
| 558 task_runner, policy_list, kRegistryChromePolicyKey, this)); | 636 task_runner, policy_list, kRegistryChromePolicyKey, this)); |
| 637 InstallTestChromePolicySchema(loader.get()); |
| 559 return new AsyncPolicyProvider(loader.Pass()); | 638 return new AsyncPolicyProvider(loader.Pass()); |
| 560 } | 639 } |
| 561 | 640 |
| 562 void PRegTestHarness::InstallEmptyPolicy() {} | 641 void PRegTestHarness::InstallEmptyPolicy() {} |
| 563 | 642 |
| 564 void PRegTestHarness::InstallStringPolicy(const std::string& policy_name, | 643 void PRegTestHarness::InstallStringPolicy(const std::string& policy_name, |
| 565 const std::string& policy_value) { | 644 const std::string& policy_value) { |
| 566 AppendStringToPRegFile(kRegistryChromePolicyKey, policy_name, policy_value); | 645 AppendStringToPRegFile(kRegistryChromePolicyKey, policy_name, policy_value); |
| 567 } | 646 } |
| 568 | 647 |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 819 gpo->dwOptions = options; | 898 gpo->dwOptions = options; |
| 820 gpo->lpFileSysPath = const_cast<wchar_t*>(path.value().c_str()); | 899 gpo->lpFileSysPath = const_cast<wchar_t*>(path.value().c_str()); |
| 821 gpo->pNext = next; | 900 gpo->pNext = next; |
| 822 gpo->pPrev = prev; | 901 gpo->pPrev = prev; |
| 823 } | 902 } |
| 824 | 903 |
| 825 bool Matches(const PolicyBundle& expected) { | 904 bool Matches(const PolicyBundle& expected) { |
| 826 PolicyLoaderWin loader(loop_.message_loop_proxy(), | 905 PolicyLoaderWin loader(loop_.message_loop_proxy(), |
| 827 &test_policy_definitions::kList, kTestPolicyKey, | 906 &test_policy_definitions::kList, kTestPolicyKey, |
| 828 this); | 907 this); |
| 908 InstallTestChromePolicySchema(&loader); |
| 909 loader.Init(base::Bind(&DoNothing)); |
| 829 scoped_ptr<PolicyBundle> loaded(loader.Load()); | 910 scoped_ptr<PolicyBundle> loaded(loader.Load()); |
| 830 return loaded->Equals(expected); | 911 return loaded->Equals(expected); |
| 831 } | 912 } |
| 832 | 913 |
| 833 void InstallRegistrySentinel() { | 914 void InstallRegistrySentinel() { |
| 834 RegKey hklm_key(HKEY_CURRENT_USER, kTestPolicyKey, KEY_ALL_ACCESS); | 915 RegKey hklm_key(HKEY_CURRENT_USER, kTestPolicyKey, KEY_ALL_ACCESS); |
| 835 ASSERT_TRUE(hklm_key.Valid()); | 916 ASSERT_TRUE(hklm_key.Valid()); |
| 836 hklm_key.WriteValue( | 917 hklm_key.WriteValue( |
| 837 UTF8ToUTF16(test_policy_definitions::kKeyString).c_str(), | 918 UTF8ToUTF16(test_policy_definitions::kKeyString).c_str(), |
| 838 UTF8ToUTF16("registry").c_str()); | 919 UTF8ToUTF16("registry").c_str()); |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1023 EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema)); | 1104 EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema)); |
| 1024 EXPECT_TRUE( | 1105 EXPECT_TRUE( |
| 1025 InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory)); | 1106 InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory)); |
| 1026 | 1107 |
| 1027 PolicyBundle expected; | 1108 PolicyBundle expected; |
| 1028 expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "int")) | 1109 expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "int")) |
| 1029 .LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER); | 1110 .LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER); |
| 1030 EXPECT_TRUE(Matches(expected)); | 1111 EXPECT_TRUE(Matches(expected)); |
| 1031 } | 1112 } |
| 1032 | 1113 |
| 1033 TEST_F(PolicyLoaderWinTest, DefaultPropertySchemaType) { | |
| 1034 // Build a schema for an "object" with a default schema for its properties. | |
| 1035 base::DictionaryValue default_schema; | |
| 1036 default_schema.SetString(schema::kType, "number"); | |
| 1037 base::DictionaryValue integer_schema; | |
| 1038 integer_schema.SetString(schema::kType, "integer"); | |
| 1039 base::DictionaryValue properties; | |
| 1040 properties.Set("special-int1", integer_schema.DeepCopy()); | |
| 1041 properties.Set("special-int2", integer_schema.DeepCopy()); | |
| 1042 base::DictionaryValue schema; | |
| 1043 schema.SetString(schema::kType, "object"); | |
| 1044 schema.Set(schema::kProperties, properties.DeepCopy()); | |
| 1045 schema.Set(schema::kAdditionalProperties, default_schema.DeepCopy()); | |
| 1046 | |
| 1047 const string16 kPathSuffix = | |
| 1048 kTestPolicyKey + ASCIIToUTF16("\\3rdparty\\extensions\\test"); | |
| 1049 EXPECT_TRUE(WriteSchema(schema, HKEY_CURRENT_USER, kPathSuffix, kSchema)); | |
| 1050 | |
| 1051 // Write some test values. | |
| 1052 base::DictionaryValue policy; | |
| 1053 // These special values have a specific schema for them. | |
| 1054 policy.SetInteger("special-int1", 123); | |
| 1055 policy.SetString("special-int2", "-456"); | |
| 1056 // Other values default to be loaded as doubles. | |
| 1057 policy.SetInteger("double1", 789.0); | |
| 1058 policy.SetString("double2", "123.456e7"); | |
| 1059 policy.SetString("invalid", "omg"); | |
| 1060 EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory)); | |
| 1061 | |
| 1062 base::DictionaryValue expected_policy; | |
| 1063 expected_policy.SetInteger("special-int1", 123); | |
| 1064 expected_policy.SetInteger("special-int2", -456); | |
| 1065 expected_policy.SetDouble("double1", 789.0); | |
| 1066 expected_policy.SetDouble("double2", 123.456e7); | |
| 1067 expected_policy.Set("invalid", base::Value::CreateNullValue()); | |
| 1068 PolicyBundle expected; | |
| 1069 expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "test")) | |
| 1070 .LoadFrom(&expected_policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER); | |
| 1071 EXPECT_TRUE(Matches(expected)); | |
| 1072 } | |
| 1073 | |
| 1074 TEST_F(PolicyLoaderWinTest, AppliedPolicyNotPresent) { | 1114 TEST_F(PolicyLoaderWinTest, AppliedPolicyNotPresent) { |
| 1075 InstallRegistrySentinel(); | 1115 InstallRegistrySentinel(); |
| 1076 gpo_list_ = NULL; | 1116 gpo_list_ = NULL; |
| 1077 gpo_list_status_ = ERROR_SUCCESS; | 1117 gpo_list_status_ = ERROR_SUCCESS; |
| 1078 | 1118 |
| 1079 PolicyBundle empty; | 1119 PolicyBundle empty; |
| 1080 EXPECT_TRUE(Matches(empty)); | 1120 EXPECT_TRUE(Matches(empty)); |
| 1081 } | 1121 } |
| 1082 | 1122 |
| 1083 TEST_F(PolicyLoaderWinTest, AppliedPolicyEmpty) { | 1123 TEST_F(PolicyLoaderWinTest, AppliedPolicyEmpty) { |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1197 .LoadFrom(&expected_a, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE); | 1237 .LoadFrom(&expected_a, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE); |
| 1198 base::DictionaryValue expected_b; | 1238 base::DictionaryValue expected_b; |
| 1199 expected_b.SetInteger("policy 1", 2); | 1239 expected_b.SetInteger("policy 1", 2); |
| 1200 expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, | 1240 expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, |
| 1201 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) | 1241 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) |
| 1202 .LoadFrom(&expected_b, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE); | 1242 .LoadFrom(&expected_b, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE); |
| 1203 EXPECT_TRUE(Matches(expected)); | 1243 EXPECT_TRUE(Matches(expected)); |
| 1204 } | 1244 } |
| 1205 | 1245 |
| 1206 } // namespace policy | 1246 } // namespace policy |
| OLD | NEW |