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 "chromeos/network/onc/onc_merger.h" 6 7 #include <set> 8 #include <string> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/logging.h" 13 #include "base/values.h" 14 #include "chromeos/network/onc/onc_signature.h" 15 #include "components/onc/onc_constants.h" 16 17 namespace chromeos { 18 namespace onc { 19 namespace { 20 21 typedef scoped_ptr<base::DictionaryValue> DictionaryPtr; 22 23 // Inserts |true| at every field name in |result| that is recommended in 24 // |policy|. 25 void MarkRecommendedFieldnames(const base::DictionaryValue& policy, 26 base::DictionaryValue* result) { 27 const base::ListValue* recommended_value = NULL; 28 if (!policy.GetListWithoutPathExpansion(::onc::kRecommended, 29 &recommended_value)) 30 return; 31 for (base::ListValue::const_iterator it = recommended_value->begin(); 32 it != recommended_value->end(); ++it) { 33 std::string entry; 34 if ((*it)->GetAsString(&entry)) 35 result->SetBooleanWithoutPathExpansion(entry, true); 36 } 37 } 38 39 // Returns a dictionary which contains |true| at each path that is editable by 40 // the user. No other fields are set. 41 DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) { 42 DictionaryPtr result_editable(new base::DictionaryValue); 43 MarkRecommendedFieldnames(policy, result_editable.get()); 44 45 // Recurse into nested dictionaries. 46 for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd(); 47 it.Advance()) { 48 const base::DictionaryValue* child_policy = NULL; 49 if (it.key() == ::onc::kRecommended || 50 !it.value().GetAsDictionary(&child_policy)) { 51 continue; 52 } 53 54 result_editable->SetWithoutPathExpansion( 55 it.key(), GetEditableFlags(*child_policy).release()); 56 } 57 return result_editable.Pass(); 58 } 59 60 // This is the base class for merging a list of DictionaryValues in 61 // parallel. See MergeDictionaries function. 62 class MergeListOfDictionaries { 63 public: 64 typedef std::vector<const base::DictionaryValue*> DictPtrs; 65 66 MergeListOfDictionaries() { 67 } 68 69 virtual ~MergeListOfDictionaries() { 70 } 71 72 // For each path in any of the dictionaries |dicts|, the function 73 // MergeListOfValues is called with the list of values that are located at 74 // that path in each of the dictionaries. This function returns a new 75 // dictionary containing all results of MergeListOfValues at the respective 76 // paths. The resulting dictionary doesn't contain empty dictionaries. 77 DictionaryPtr MergeDictionaries(const DictPtrs &dicts) { 78 DictionaryPtr result(new base::DictionaryValue); 79 std::set<std::string> visited; 80 for (DictPtrs::const_iterator it_outer = dicts.begin(); 81 it_outer != dicts.end(); ++it_outer) { 82 if (!*it_outer) 83 continue; 84 85 for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd(); 86 field.Advance()) { 87 const std::string& key = field.key(); 88 if (key == ::onc::kRecommended || !visited.insert(key).second) 89 continue; 90 91 scoped_ptr<base::Value> merged_value; 92 if (field.value().IsType(base::Value::TYPE_DICTIONARY)) { 93 DictPtrs nested_dicts; 94 for (DictPtrs::const_iterator it_inner = dicts.begin(); 95 it_inner != dicts.end(); ++it_inner) { 96 const base::DictionaryValue* nested_dict = NULL; 97 if (*it_inner) 98 (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict); 99 nested_dicts.push_back(nested_dict); 100 } 101 DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts)); 102 if (!merged_dict->empty()) 103 merged_value = merged_dict.Pass(); 104 } else { 105 std::vector<const base::Value*> values; 106 for (DictPtrs::const_iterator it_inner = dicts.begin(); 107 it_inner != dicts.end(); ++it_inner) { 108 const base::Value* value = NULL; 109 if (*it_inner) 110 (*it_inner)->GetWithoutPathExpansion(key, &value); 111 values.push_back(value); 112 } 113 merged_value = MergeListOfValues(key, values); 114 } 115 116 if (merged_value) 117 result->SetWithoutPathExpansion(key, merged_value.release()); 118 } 119 } 120 return result.Pass(); 121 } 122 123 protected: 124 // This function is called by MergeDictionaries for each list of values that 125 // are located at the same path in each of the dictionaries. The order of the 126 // values is the same as of the given dictionaries |dicts|. If a dictionary 127 // doesn't contain a path then it's value is NULL. 128 virtual scoped_ptr<base::Value> MergeListOfValues( 129 const std::string& key, 130 const std::vector<const base::Value*>& values) = 0; 131 132 virtual DictionaryPtr MergeNestedDictionaries(const std::string& key, 133 const DictPtrs &dicts) { 134 return MergeDictionaries(dicts); 135 } 136 137 private: 138 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries); 139 }; 140 141 // This is the base class for merging policies and user settings. 142 class MergeSettingsAndPolicies : public MergeListOfDictionaries { 143 public: 144 struct ValueParams { 145 const base::Value* user_policy; 146 const base::Value* device_policy; 147 const base::Value* user_setting; 148 const base::Value* shared_setting; 149 const base::Value* active_setting; 150 bool user_editable; 151 bool device_editable; 152 }; 153 154 MergeSettingsAndPolicies() {} 155 156 // Merge the provided dictionaries. For each path in any of the dictionaries, 157 // MergeValues is called. Its results are collected in a new dictionary which 158 // is then returned. The resulting dictionary never contains empty 159 // dictionaries. 160 DictionaryPtr MergeDictionaries( 161 const base::DictionaryValue* user_policy, 162 const base::DictionaryValue* device_policy, 163 const base::DictionaryValue* user_settings, 164 const base::DictionaryValue* shared_settings, 165 const base::DictionaryValue* active_settings) { 166 hasUserPolicy_ = (user_policy != NULL); 167 hasDevicePolicy_ = (device_policy != NULL); 168 169 DictionaryPtr user_editable; 170 if (user_policy != NULL) 171 user_editable = GetEditableFlags(*user_policy); 172 173 DictionaryPtr device_editable; 174 if (device_policy != NULL) 175 device_editable = GetEditableFlags(*device_policy); 176 177 std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL); 178 dicts[kUserPolicyIndex] = user_policy; 179 dicts[kDevicePolicyIndex] = device_policy; 180 dicts[kUserSettingsIndex] = user_settings; 181 dicts[kSharedSettingsIndex] = shared_settings; 182 dicts[kActiveSettingsIndex] = active_settings; 183 dicts[kUserEditableIndex] = user_editable.get(); 184 dicts[kDeviceEditableIndex] = device_editable.get(); 185 return MergeListOfDictionaries::MergeDictionaries(dicts); 186 } 187 188 protected: 189 // This function is called by MergeDictionaries for each list of values that 190 // are located at the same path in each of the dictionaries. Implementations 191 // can use the Has*Policy functions. 192 virtual scoped_ptr<base::Value> MergeValues(const std::string& key, 193 const ValueParams& values) = 0; 194 195 // Whether a user policy was provided. 196 bool HasUserPolicy() { 197 return hasUserPolicy_; 198 } 199 200 // Whether a device policy was provided. 201 bool HasDevicePolicy() { 202 return hasDevicePolicy_; 203 } 204 205 // MergeListOfDictionaries override. 206 virtual scoped_ptr<base::Value> MergeListOfValues( 207 const std::string& key, 208 const std::vector<const base::Value*>& values) OVERRIDE { 209 bool user_editable = !HasUserPolicy(); 210 if (values[kUserEditableIndex]) 211 values[kUserEditableIndex]->GetAsBoolean(&user_editable); 212 213 bool device_editable = !HasDevicePolicy(); 214 if (values[kDeviceEditableIndex]) 215 values[kDeviceEditableIndex]->GetAsBoolean(&device_editable); 216 217 ValueParams params; 218 params.user_policy = values[kUserPolicyIndex]; 219 params.device_policy = values[kDevicePolicyIndex]; 220 params.user_setting = values[kUserSettingsIndex]; 221 params.shared_setting = values[kSharedSettingsIndex]; 222 params.active_setting = values[kActiveSettingsIndex]; 223 params.user_editable = user_editable; 224 params.device_editable = device_editable; 225 return MergeValues(key, params); 226 } 227 228 private: 229 enum { 230 kUserPolicyIndex, 231 kDevicePolicyIndex, 232 kUserSettingsIndex, 233 kSharedSettingsIndex, 234 kActiveSettingsIndex, 235 kUserEditableIndex, 236 kDeviceEditableIndex, 237 kLastIndex 238 }; 239 240 bool hasUserPolicy_, hasDevicePolicy_; 241 242 DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies); 243 }; 244 245 // Call MergeDictionaries to merge policies and settings to the effective 246 // values. This ignores the active settings of Shill. See the description of 247 // MergeSettingsAndPoliciesToEffective. 248 class MergeToEffective : public MergeSettingsAndPolicies { 249 public: 250 MergeToEffective() {} 251 252 protected: 253 // Merges |values| to the effective value (Mandatory policy overwrites user 254 // settings overwrites shared settings overwrites recommended policy). |which| 255 // is set to the respective onc::kAugmentation* constant that indicates which 256 // source of settings is effective. Note that this function may return a NULL 257 // pointer and set |which| to ::onc::kAugmentationUserPolicy, which means that 258 // the 259 // user policy didn't set a value but also didn't recommend it, thus enforcing 260 // the empty value. 261 scoped_ptr<base::Value> MergeValues(const std::string& key, 262 const ValueParams& values, 263 std::string* which) { 264 const base::Value* result = NULL; 265 which->clear(); 266 if (!values.user_editable) { 267 result = values.user_policy; 268 *which = ::onc::kAugmentationUserPolicy; 269 } else if (!values.device_editable) { 270 result = values.device_policy; 271 *which = ::onc::kAugmentationDevicePolicy; 272 } else if (values.user_setting) { 273 result = values.user_setting; 274 *which = ::onc::kAugmentationUserSetting; 275 } else if (values.shared_setting) { 276 result = values.shared_setting; 277 *which = ::onc::kAugmentationSharedSetting; 278 } else if (values.user_policy) { 279 result = values.user_policy; 280 *which = ::onc::kAugmentationUserPolicy; 281 } else if (values.device_policy) { 282 result = values.device_policy; 283 *which = ::onc::kAugmentationDevicePolicy; 284 } else { 285 // Can be reached if the current field is recommended, but none of the 286 // dictionaries contained a value for it. 287 } 288 if (result) 289 return make_scoped_ptr(result->DeepCopy()); 290 return scoped_ptr<base::Value>(); 291 } 292 293 // MergeSettingsAndPolicies override. 294 virtual scoped_ptr<base::Value> MergeValues( 295 const std::string& key, 296 const ValueParams& values) OVERRIDE { 297 std::string which; 298 return MergeValues(key, values, &which); 299 } 300 301 private: 302 DISALLOW_COPY_AND_ASSIGN(MergeToEffective); 303 }; 304 305 // Call MergeDictionaries to merge policies and settings to an augmented 306 // dictionary which contains a dictionary for each value in the original 307 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented. 308 class MergeToAugmented : public MergeToEffective { 309 public: 310 MergeToAugmented() {} 311 312 DictionaryPtr MergeDictionaries( 313 const OncValueSignature& signature, 314 const base::DictionaryValue* user_policy, 315 const base::DictionaryValue* device_policy, 316 const base::DictionaryValue* user_settings, 317 const base::DictionaryValue* shared_settings, 318 const base::DictionaryValue* active_settings) { 319 signature_ = &signature; 320 return MergeToEffective::MergeDictionaries(user_policy, 321 device_policy, 322 user_settings, 323 shared_settings, 324 active_settings); 325 } 326 327 protected: 328 // MergeSettingsAndPolicies override. 329 virtual scoped_ptr<base::Value> MergeValues( 330 const std::string& key, 331 const ValueParams& values) OVERRIDE { 332 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue); 333 if (values.active_setting) { 334 result->SetWithoutPathExpansion(::onc::kAugmentationActiveSetting, 335 values.active_setting->DeepCopy()); 336 } 337 338 const OncFieldSignature* field = NULL; 339 if (signature_) 340 field = GetFieldSignature(*signature_, key); 341 342 if (field) { 343 // This field is part of the provided ONCSignature, thus it can be 344 // controlled by policy. 345 std::string which_effective; 346 MergeToEffective::MergeValues(key, values, &which_effective).reset(); 347 if (!which_effective.empty()) { 348 result->SetStringWithoutPathExpansion( 349 ::onc::kAugmentationEffectiveSetting, which_effective); 350 } 351 bool is_credential = onc::FieldIsCredential(*signature_, key); 352 353 // Prevent credentials from being forwarded in cleartext to 354 // UI. User/shared credentials are not stored separately, so they cannot 355 // leak here. 356 if (!is_credential) { 357 if (values.user_policy) { 358 result->SetWithoutPathExpansion(::onc::kAugmentationUserPolicy, 359 values.user_policy->DeepCopy()); 360 } 361 if (values.device_policy) { 362 result->SetWithoutPathExpansion(::onc::kAugmentationDevicePolicy, 363 values.device_policy->DeepCopy()); 364 } 365 } 366 if (values.user_setting) { 367 result->SetWithoutPathExpansion(::onc::kAugmentationUserSetting, 368 values.user_setting->DeepCopy()); 369 } 370 if (values.shared_setting) { 371 result->SetWithoutPathExpansion(::onc::kAugmentationSharedSetting, 372 values.shared_setting->DeepCopy()); 373 } 374 if (HasUserPolicy() && values.user_editable) { 375 result->SetBooleanWithoutPathExpansion(::onc::kAugmentationUserEditable, 376 true); 377 } 378 if (HasDevicePolicy() && values.device_editable) { 379 result->SetBooleanWithoutPathExpansion( 380 ::onc::kAugmentationDeviceEditable, true); 381 } 382 } else { 383 // This field is not part of the provided ONCSignature, thus it cannot be 384 // controlled by policy. 385 result->SetStringWithoutPathExpansion( 386 ::onc::kAugmentationEffectiveSetting, ::onc::kAugmentationUnmanaged); 387 } 388 if (result->empty()) 389 result.reset(); 390 return result.PassAs<base::Value>(); 391 } 392 393 // MergeListOfDictionaries override. 394 virtual DictionaryPtr MergeNestedDictionaries( 395 const std::string& key, 396 const DictPtrs &dicts) OVERRIDE { 397 DictionaryPtr result; 398 if (signature_) { 399 const OncValueSignature* enclosing_signature = signature_; 400 signature_ = NULL; 401 402 const OncFieldSignature* field = 403 GetFieldSignature(*enclosing_signature, key); 404 if (field) 405 signature_ = field->value_signature; 406 result = MergeToEffective::MergeNestedDictionaries(key, dicts); 407 408 signature_ = enclosing_signature; 409 } else { 410 result = MergeToEffective::MergeNestedDictionaries(key, dicts); 411 } 412 return result.Pass(); 413 } 414 415 private: 416 const OncValueSignature* signature_; 417 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented); 418 }; 419 420 } // namespace 421 422 DictionaryPtr MergeSettingsAndPoliciesToEffective( 423 const base::DictionaryValue* user_policy, 424 const base::DictionaryValue* device_policy, 425 const base::DictionaryValue* user_settings, 426 const base::DictionaryValue* shared_settings) { 427 MergeToEffective merger; 428 return merger.MergeDictionaries( 429 user_policy, device_policy, user_settings, shared_settings, NULL); 430 } 431 432 DictionaryPtr MergeSettingsAndPoliciesToAugmented( 433 const OncValueSignature& signature, 434 const base::DictionaryValue* user_policy, 435 const base::DictionaryValue* device_policy, 436 const base::DictionaryValue* user_settings, 437 const base::DictionaryValue* shared_settings, 438 const base::DictionaryValue* active_settings) { 439 MergeToAugmented merger; 440 return merger.MergeDictionaries( 441 signature, user_policy, device_policy, user_settings, shared_settings, 442 active_settings); 443 } 444 445 } // namespace onc 446 } // namespace chromeos 447