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 3 // found in the LICENSE file. 4 5 #include <CoreFoundation/CoreFoundation.h> 6 7 #include "base/basictypes.h" 8 #include "base/callback.h" 9 #include "base/mac/scoped_cftyperef.h" 10 #include "base/strings/sys_string_conversions.h" 11 #include "base/values.h" 12 #include "chrome/browser/policy/async_policy_provider.h" 13 #include "chrome/browser/policy/configuration_policy_provider_test.h" 14 #include "chrome/browser/policy/external_data_fetcher.h" 15 #include "chrome/browser/policy/policy_bundle.h" 16 #include "chrome/browser/policy/policy_loader_mac.h" 17 #include "chrome/browser/policy/policy_map.h" 18 #include "chrome/browser/policy/preferences_mock_mac.h" 19 #include "policy/policy_constants.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 22 using base::ScopedCFTypeRef; 23 24 namespace policy { 25 26 namespace { 27 28 // Converts a base::Value to the equivalent CFPropertyListRef. 29 // The returned value is owned by the caller. 30 CFPropertyListRef CreatePropertyFromValue(const base::Value* value) { 31 switch (value->GetType()) { 32 case base::Value::TYPE_NULL: 33 return kCFNull; 34 35 case base::Value::TYPE_BOOLEAN: { 36 bool bool_value; 37 if (value->GetAsBoolean(&bool_value)) 38 return bool_value ? kCFBooleanTrue : kCFBooleanFalse; 39 break; 40 } 41 42 case base::Value::TYPE_INTEGER: { 43 int int_value; 44 if (value->GetAsInteger(&int_value)) { 45 return CFNumberCreate( 46 kCFAllocatorDefault, kCFNumberIntType, &int_value); 47 } 48 break; 49 } 50 51 case base::Value::TYPE_DOUBLE: { 52 double double_value; 53 if (value->GetAsDouble(&double_value)) { 54 return CFNumberCreate( 55 kCFAllocatorDefault, kCFNumberDoubleType, &double_value); 56 } 57 break; 58 } 59 60 case base::Value::TYPE_STRING: { 61 std::string string_value; 62 if (value->GetAsString(&string_value)) 63 return base::SysUTF8ToCFStringRef(string_value); 64 break; 65 } 66 67 case base::Value::TYPE_DICTIONARY: { 68 const base::DictionaryValue* dict_value; 69 if (value->GetAsDictionary(&dict_value)) { 70 // |dict| is owned by the caller. 71 CFMutableDictionaryRef dict = 72 CFDictionaryCreateMutable(kCFAllocatorDefault, 73 dict_value->size(), 74 &kCFTypeDictionaryKeyCallBacks, 75 &kCFTypeDictionaryValueCallBacks); 76 for (base::DictionaryValue::Iterator iterator(*dict_value); 77 !iterator.IsAtEnd(); iterator.Advance()) { 78 // CFDictionaryAddValue() retains both |key| and |value|, so make sure 79 // the references are balanced. 80 ScopedCFTypeRef<CFStringRef> key( 81 base::SysUTF8ToCFStringRef(iterator.key())); 82 ScopedCFTypeRef<CFPropertyListRef> cf_value( 83 CreatePropertyFromValue(&iterator.value())); 84 if (cf_value) 85 CFDictionaryAddValue(dict, key, cf_value); 86 } 87 return dict; 88 } 89 break; 90 } 91 92 case base::Value::TYPE_LIST: { 93 const base::ListValue* list; 94 if (value->GetAsList(&list)) { 95 CFMutableArrayRef array = 96 CFArrayCreateMutable(NULL, list->GetSize(), &kCFTypeArrayCallBacks); 97 for (base::ListValue::const_iterator it(list->begin()); 98 it != list->end(); ++it) { 99 // CFArrayAppendValue() retains |value|, so make sure the reference 100 // created by CreatePropertyFromValue() is released. 101 ScopedCFTypeRef<CFPropertyListRef> cf_value( 102 CreatePropertyFromValue(*it)); 103 if (cf_value) 104 CFArrayAppendValue(array, cf_value); 105 } 106 return array; 107 } 108 break; 109 } 110 111 case base::Value::TYPE_BINARY: 112 // This type isn't converted (though it can be represented as CFData) 113 // because there's no equivalent JSON type, and policy values can only 114 // take valid JSON values. 115 break; 116 } 117 118 return NULL; 119 } 120 121 class TestHarness : public PolicyProviderTestHarness { 122 public: 123 TestHarness(); 124 virtual ~TestHarness(); 125 126 virtual void SetUp() OVERRIDE; 127 128 virtual ConfigurationPolicyProvider* CreateProvider( 129 const PolicyDefinitionList* policy_definition_list) OVERRIDE; 130 131 virtual void InstallEmptyPolicy() OVERRIDE; 132 virtual void InstallStringPolicy(const std::string& policy_name, 133 const std::string& policy_value) OVERRIDE; 134 virtual void InstallIntegerPolicy(const std::string& policy_name, 135 int policy_value) OVERRIDE; 136 virtual void InstallBooleanPolicy(const std::string& policy_name, 137 bool policy_value) OVERRIDE; 138 virtual void InstallStringListPolicy( 139 const std::string& policy_name, 140 const base::ListValue* policy_value) OVERRIDE; 141 virtual void InstallDictionaryPolicy( 142 const std::string& policy_name, 143 const base::DictionaryValue* policy_value) OVERRIDE; 144 145 static PolicyProviderTestHarness* Create(); 146 147 private: 148 MockPreferences* prefs_; 149 150 DISALLOW_COPY_AND_ASSIGN(TestHarness); 151 }; 152 153 TestHarness::TestHarness() 154 : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER) {} 155 156 TestHarness::~TestHarness() {} 157 158 void TestHarness::SetUp() {} 159 160 ConfigurationPolicyProvider* TestHarness::CreateProvider( 161 const PolicyDefinitionList* policy_definition_list) { 162 prefs_ = new MockPreferences(); 163 scoped_ptr<AsyncPolicyLoader> loader( 164 new PolicyLoaderMac(policy_definition_list, prefs_)); 165 return new AsyncPolicyProvider(loader.Pass()); 166 } 167 168 void TestHarness::InstallEmptyPolicy() {} 169 170 void TestHarness::InstallStringPolicy(const std::string& policy_name, 171 const std::string& policy_value) { 172 ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name)); 173 ScopedCFTypeRef<CFStringRef> value(base::SysUTF8ToCFStringRef(policy_value)); 174 prefs_->AddTestItem(name, value, true); 175 } 176 177 void TestHarness::InstallIntegerPolicy(const std::string& policy_name, 178 int policy_value) { 179 ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name)); 180 ScopedCFTypeRef<CFNumberRef> value( 181 CFNumberCreate(NULL, kCFNumberIntType, &policy_value)); 182 prefs_->AddTestItem(name, value, true); 183 } 184 185 void TestHarness::InstallBooleanPolicy(const std::string& policy_name, 186 bool policy_value) { 187 ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name)); 188 prefs_->AddTestItem(name, 189 policy_value ? kCFBooleanTrue : kCFBooleanFalse, 190 true); 191 } 192 193 void TestHarness::InstallStringListPolicy(const std::string& policy_name, 194 const base::ListValue* policy_value) { 195 ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name)); 196 ScopedCFTypeRef<CFPropertyListRef> array( 197 CreatePropertyFromValue(policy_value)); 198 ASSERT_TRUE(array); 199 prefs_->AddTestItem(name, array, true); 200 } 201 202 void TestHarness::InstallDictionaryPolicy( 203 const std::string& policy_name, 204 const base::DictionaryValue* policy_value) { 205 ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name)); 206 ScopedCFTypeRef<CFPropertyListRef> dict( 207 CreatePropertyFromValue(policy_value)); 208 ASSERT_TRUE(dict); 209 prefs_->AddTestItem(name, dict, true); 210 } 211 212 // static 213 PolicyProviderTestHarness* TestHarness::Create() { 214 return new TestHarness(); 215 } 216 217 } // namespace 218 219 // Instantiate abstract test case for basic policy reading tests. 220 INSTANTIATE_TEST_CASE_P( 221 PolicyProviderMacTest, 222 ConfigurationPolicyProviderTest, 223 testing::Values(TestHarness::Create)); 224 225 // TODO(joaodasilva): instantiate Configuration3rdPartyPolicyProviderTest too 226 // once the mac loader supports 3rd party policy. http://crbug.com/108995 227 228 // Special test cases for some mac preferences details. 229 class PolicyLoaderMacTest : public PolicyTestBase { 230 protected: 231 PolicyLoaderMacTest() 232 : prefs_(new MockPreferences()), 233 loader_(new PolicyLoaderMac(&test_policy_definitions::kList, prefs_)), 234 provider_(scoped_ptr<AsyncPolicyLoader>(loader_)) {} 235 virtual ~PolicyLoaderMacTest() {} 236 237 virtual void SetUp() OVERRIDE { 238 PolicyTestBase::SetUp(); 239 provider_.Init(); 240 } 241 242 virtual void TearDown() OVERRIDE { 243 provider_.Shutdown(); 244 PolicyTestBase::TearDown(); 245 } 246 247 MockPreferences* prefs_; 248 PolicyLoaderMac* loader_; 249 AsyncPolicyProvider provider_; 250 }; 251 252 TEST_F(PolicyLoaderMacTest, Invalid) { 253 ScopedCFTypeRef<CFStringRef> name( 254 base::SysUTF8ToCFStringRef(test_policy_definitions::kKeyString)); 255 const char buffer[] = "binary \xde\xad\xbe\xef data"; 256 ScopedCFTypeRef<CFDataRef> invalid_data( 257 CFDataCreate(kCFAllocatorDefault, 258 reinterpret_cast<const UInt8 *>(buffer), 259 arraysize(buffer))); 260 ASSERT_TRUE(invalid_data); 261 prefs_->AddTestItem(name, invalid_data.get(), true); 262 prefs_->AddTestItem(name, invalid_data.get(), false); 263 264 // Make the provider read the updated |prefs_|. 265 provider_.RefreshPolicies(); 266 loop_.RunUntilIdle(); 267 const PolicyBundle kEmptyBundle; 268 EXPECT_TRUE(provider_.policies().Equals(kEmptyBundle)); 269 } 270 271 TEST_F(PolicyLoaderMacTest, TestNonForcedValue) { 272 ScopedCFTypeRef<CFStringRef> name( 273 base::SysUTF8ToCFStringRef(test_policy_definitions::kKeyString)); 274 ScopedCFTypeRef<CFPropertyListRef> test_value( 275 base::SysUTF8ToCFStringRef("string value")); 276 ASSERT_TRUE(test_value.get()); 277 prefs_->AddTestItem(name, test_value.get(), false); 278 279 // Make the provider read the updated |prefs_|. 280 provider_.RefreshPolicies(); 281 loop_.RunUntilIdle(); 282 PolicyBundle expected_bundle; 283 expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())) 284 .Set(test_policy_definitions::kKeyString, 285 POLICY_LEVEL_RECOMMENDED, 286 POLICY_SCOPE_USER, 287 base::Value::CreateStringValue("string value"), 288 NULL); 289 EXPECT_TRUE(provider_.policies().Equals(expected_bundle)); 290 } 291 292 TEST_F(PolicyLoaderMacTest, TestConversions) { 293 base::DictionaryValue root; 294 295 // base::Value::TYPE_NULL 296 root.Set("null", base::Value::CreateNullValue()); 297 298 // base::Value::TYPE_BOOLEAN 299 root.SetBoolean("false", false); 300 root.SetBoolean("true", true); 301 302 // base::Value::TYPE_INTEGER 303 root.SetInteger("int", 123); 304 root.SetInteger("zero", 0); 305 306 // base::Value::TYPE_DOUBLE 307 root.SetDouble("double", 123.456); 308 root.SetDouble("zerod", 0.0); 309 310 // base::Value::TYPE_STRING 311 root.SetString("string", "the fox jumps over something"); 312 root.SetString("empty", ""); 313 314 // base::Value::TYPE_LIST 315 base::ListValue list; 316 root.Set("emptyl", list.DeepCopy()); 317 for (base::DictionaryValue::Iterator it(root); !it.IsAtEnd(); it.Advance()) 318 list.Append(it.value().DeepCopy()); 319 EXPECT_EQ(root.size(), list.GetSize()); 320 list.Append(root.DeepCopy()); 321 root.Set("list", list.DeepCopy()); 322 323 // base::Value::TYPE_DICTIONARY 324 base::DictionaryValue dict; 325 root.Set("emptyd", dict.DeepCopy()); 326 // Very meta. 327 root.Set("dict", root.DeepCopy()); 328 329 ScopedCFTypeRef<CFPropertyListRef> property(CreatePropertyFromValue(&root)); 330 ASSERT_TRUE(property); 331 scoped_ptr<base::Value> value( 332 PolicyLoaderMac::CreateValueFromProperty(property)); 333 ASSERT_TRUE(value.get()); 334 335 EXPECT_TRUE(root.Equals(value.get())); 336 } 337 338 } // namespace policy 339