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