onc_merger.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 16namespace chromeos { 17namespace onc { 18namespace { 19 20typedef scoped_ptr<base::DictionaryValue> DictionaryPtr; 21 22// Inserts |true| at every field name in |result| that is recommended in 23// |policy|. 24void MarkRecommendedFieldnames(const base::DictionaryValue& policy, 25 base::DictionaryValue* result) { 26 const ListValue* recommended_value = NULL; 27 if (!policy.GetListWithoutPathExpansion(kRecommended, &recommended_value)) 28 return; 29 for (ListValue::const_iterator it = recommended_value->begin(); 30 it != recommended_value->end(); ++it) { 31 std::string entry; 32 if ((*it)->GetAsString(&entry)) 33 result->SetBooleanWithoutPathExpansion(entry, true); 34 } 35} 36 37// Returns a dictionary which contains |true| at each path that is editable by 38// the user. No other fields are set. 39DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) { 40 DictionaryPtr result_editable(new base::DictionaryValue); 41 MarkRecommendedFieldnames(policy, result_editable.get()); 42 43 // Recurse into nested dictionaries. 44 for (base::DictionaryValue::Iterator it(policy); it.HasNext(); it.Advance()) { 45 const base::DictionaryValue* child_policy = NULL; 46 if (it.key() == kRecommended || 47 !it.value().GetAsDictionary(&child_policy)) { 48 continue; 49 } 50 51 result_editable->SetWithoutPathExpansion( 52 it.key(), GetEditableFlags(*child_policy).release()); 53 } 54 return result_editable.Pass(); 55} 56 57// This is the base class for merging a list of DictionaryValues in 58// parallel. See MergeDictionaries function. 59class MergeListOfDictionaries { 60 public: 61 typedef std::vector<const base::DictionaryValue*> DictPtrs; 62 63 MergeListOfDictionaries() { 64 } 65 66 virtual ~MergeListOfDictionaries() { 67 } 68 69 // For each path in any of the dictionaries |dicts|, the function 70 // MergeListOfValues is called with the list of values that are located at 71 // that path in each of the dictionaries. This function returns a new 72 // dictionary containing all results of MergeListOfValues at the respective 73 // paths. The resulting dictionary doesn't contain empty dictionaries. 74 DictionaryPtr MergeDictionaries(const DictPtrs &dicts) { 75 DictionaryPtr result(new base::DictionaryValue); 76 std::set<std::string> visited; 77 for (DictPtrs::const_iterator it_outer = dicts.begin(); 78 it_outer != dicts.end(); ++it_outer) { 79 if (!*it_outer) 80 continue; 81 82 for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd(); 83 field.Advance()) { 84 const std::string& key = field.key(); 85 if (key == kRecommended || !visited.insert(key).second) 86 continue; 87 88 scoped_ptr<base::Value> merged_value; 89 if (field.value().IsType(base::Value::TYPE_DICTIONARY)) { 90 DictPtrs nested_dicts; 91 for (DictPtrs::const_iterator it_inner = dicts.begin(); 92 it_inner != dicts.end(); ++it_inner) { 93 const base::DictionaryValue* nested_dict = NULL; 94 if (*it_inner) 95 (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict); 96 nested_dicts.push_back(nested_dict); 97 } 98 DictionaryPtr merged_dict(MergeDictionaries(nested_dicts)); 99 if (!merged_dict->empty()) 100 merged_value = merged_dict.Pass(); 101 } else { 102 std::vector<const base::Value*> values; 103 for (DictPtrs::const_iterator it_inner = dicts.begin(); 104 it_inner != dicts.end(); ++it_inner) { 105 const base::Value* value = NULL; 106 if (*it_inner) 107 (*it_inner)->GetWithoutPathExpansion(key, &value); 108 values.push_back(value); 109 } 110 merged_value = MergeListOfValues(values); 111 } 112 113 if (merged_value) 114 result->SetWithoutPathExpansion(key, merged_value.release()); 115 } 116 } 117 return result.Pass(); 118 } 119 120 protected: 121 // This function is called by MergeDictionaries for each list of values that 122 // are located at the same path in each of the dictionaries. The order of the 123 // values is the same as of the given dictionaries |dicts|. If a dictionary 124 // doesn't contain a path then it's value is NULL. 125 virtual scoped_ptr<base::Value> MergeListOfValues( 126 const std::vector<const base::Value*>& values) = 0; 127 128 private: 129 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries); 130}; 131 132// This is the base class for merging policies and user settings. 133class MergeSettingsAndPolicies : public MergeListOfDictionaries { 134 public: 135 struct ValueParams { 136 const base::Value* user_policy; 137 const base::Value* device_policy; 138 const base::Value* user_settings; 139 const base::Value* shared_settings; 140 bool user_editable; 141 bool device_editable; 142 }; 143 144 // Merge the provided dictionaries. For each path in any of the dictionaries, 145 // MergeValues is called. Its results are collected in a new dictionary which 146 // is then returned. The resulting dictionary never contains empty 147 // dictionaries. 148 DictionaryPtr MergeDictionaries( 149 const base::DictionaryValue* user_policy, 150 const base::DictionaryValue* device_policy, 151 const base::DictionaryValue* user_settings, 152 const base::DictionaryValue* shared_settings) { 153 hasUserPolicy_ = (user_policy != NULL); 154 hasDevicePolicy_ = (device_policy != NULL); 155 156 DictionaryPtr user_editable; 157 if (user_policy != NULL) 158 user_editable = GetEditableFlags(*user_policy); 159 160 DictionaryPtr device_editable; 161 if (device_policy != NULL) 162 device_editable = GetEditableFlags(*device_policy); 163 164 std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL); 165 dicts[kUserPolicyIndex] = user_policy; 166 dicts[kDevicePolicyIndex] = device_policy; 167 dicts[kUserSettingsIndex] = user_settings; 168 dicts[kSharedSettingsIndex] = shared_settings; 169 dicts[kUserEditableIndex] = user_editable.get(); 170 dicts[kDeviceEditableIndex] = device_editable.get(); 171 return MergeListOfDictionaries::MergeDictionaries(dicts); 172 } 173 174 protected: 175 // This function is called by MergeDictionaries for each list of values that 176 // are located at the same path in each of the dictionaries. Implementations 177 // can use the Has*Policy functions. 178 virtual scoped_ptr<base::Value> MergeValues(ValueParams values) = 0; 179 180 // Whether a user policy was provided. 181 bool HasUserPolicy() { 182 return hasUserPolicy_; 183 } 184 185 // Whether a device policy was provided. 186 bool HasDevicePolicy() { 187 return hasDevicePolicy_; 188 } 189 190 // MergeListOfDictionaries override. 191 virtual scoped_ptr<base::Value> MergeListOfValues( 192 const std::vector<const base::Value*>& values) OVERRIDE { 193 bool user_editable = !HasUserPolicy(); 194 if (values[kUserEditableIndex]) 195 values[kUserEditableIndex]->GetAsBoolean(&user_editable); 196 197 bool device_editable = !HasDevicePolicy(); 198 if (values[kDeviceEditableIndex]) 199 values[kDeviceEditableIndex]->GetAsBoolean(&device_editable); 200 201 ValueParams params; 202 params.user_policy = values[kUserPolicyIndex]; 203 params.device_policy = values[kDevicePolicyIndex]; 204 params.user_settings = values[kUserSettingsIndex]; 205 params.shared_settings = values[kSharedSettingsIndex]; 206 params.user_editable = user_editable; 207 params.device_editable = device_editable; 208 return MergeValues(params); 209 } 210 211 private: 212 enum { 213 kUserPolicyIndex, 214 kDevicePolicyIndex, 215 kUserSettingsIndex, 216 kSharedSettingsIndex, 217 kUserEditableIndex, 218 kDeviceEditableIndex, 219 kLastIndex 220 }; 221 222 bool hasUserPolicy_, hasDevicePolicy_; 223}; 224 225// Call MergeDictionaries to merge policies and settings to the effective 226// values. See the description of MergeSettingsAndPoliciesToEffective. 227class MergeToEffective : public MergeSettingsAndPolicies { 228 protected: 229 // Merges |values| to the effective value (Mandatory policy overwrites user 230 // settings overwrites shared settings overwrites recommended policy). |which| 231 // is set to the respective onc::kAugmentation* constant that indicates which 232 // source of settings is effective. Note that this function may return a NULL 233 // pointer and set |which| to kAugmentationUserPolicy, which means that the 234 // user policy didn't set a value but also didn't recommend it, thus enforcing 235 // the empty value. 236 scoped_ptr<base::Value> MergeValues(ValueParams values, std::string* which) { 237 const base::Value* result = NULL; 238 which->clear(); 239 if (!values.user_editable) { 240 result = values.user_policy; 241 *which = kAugmentationUserPolicy; 242 } else if (!values.device_editable) { 243 result = values.device_policy; 244 *which = kAugmentationDevicePolicy; 245 } else if (values.user_settings) { 246 result = values.user_settings; 247 *which = kAugmentationUserSetting; 248 } else if (values.shared_settings) { 249 result = values.shared_settings; 250 *which = kAugmentationSharedSetting; 251 } else if (values.user_policy) { 252 result = values.user_policy; 253 *which = kAugmentationUserPolicy; 254 } else if (values.device_policy) { 255 result = values.device_policy; 256 *which = kAugmentationDevicePolicy; 257 } else { 258 // Can be reached if the current field is recommended, but none of the 259 // dictionaries contained a value for it. 260 } 261 if (result) 262 return make_scoped_ptr(result->DeepCopy()); 263 return scoped_ptr<base::Value>(); 264 } 265 266 // MergeSettingsAndPolicies override. 267 virtual scoped_ptr<base::Value> MergeValues( 268 ValueParams values) OVERRIDE { 269 std::string which; 270 return MergeValues(values, &which); 271 } 272}; 273 274// Call MergeDictionaries to merge policies and settings to an augmented 275// dictionary which contains a dictionary for each value in the original 276// dictionaries. See the description of MergeSettingsAndPoliciesToAugmented. 277class MergeToAugmented : public MergeToEffective { 278 protected: 279 // MergeSettingsAndPolicies override. 280 virtual scoped_ptr<base::Value> MergeValues( 281 ValueParams values) OVERRIDE { 282 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue); 283 std::string which_effective; 284 MergeToEffective::MergeValues(values, &which_effective).reset(); 285 if (!which_effective.empty()) { 286 result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting, 287 which_effective); 288 } 289 if (values.user_policy) { 290 result->SetWithoutPathExpansion(kAugmentationUserPolicy, 291 values.user_policy->DeepCopy()); 292 } 293 if (values.device_policy) { 294 result->SetWithoutPathExpansion(kAugmentationDevicePolicy, 295 values.device_policy->DeepCopy()); 296 } 297 if (values.user_settings) { 298 result->SetWithoutPathExpansion(kAugmentationUserSetting, 299 values.user_settings->DeepCopy()); 300 } 301 if (values.shared_settings) { 302 result->SetWithoutPathExpansion(kAugmentationSharedSetting, 303 values.shared_settings->DeepCopy()); 304 } 305 if (HasUserPolicy() && values.user_editable) { 306 result->SetBooleanWithoutPathExpansion(kAugmentationUserEditable, 307 values.user_editable); 308 } 309 if (HasDevicePolicy() && values.device_editable) { 310 result->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable, 311 values.device_editable); 312 } 313 return result.PassAs<base::Value>(); 314 } 315}; 316 317} // namespace 318 319DictionaryPtr MergeSettingsAndPoliciesToEffective( 320 const base::DictionaryValue* user_policy, 321 const base::DictionaryValue* device_policy, 322 const base::DictionaryValue* user_settings, 323 const base::DictionaryValue* shared_settings) { 324 MergeToEffective merger; 325 return merger.MergeDictionaries( 326 user_policy, device_policy, user_settings, shared_settings); 327} 328 329DictionaryPtr MergeSettingsAndPoliciesToAugmented( 330 const base::DictionaryValue* user_policy, 331 const base::DictionaryValue* device_policy, 332 const base::DictionaryValue* user_settings, 333 const base::DictionaryValue* shared_settings) { 334 MergeToAugmented merger; 335 return merger.MergeDictionaries( 336 user_policy, device_policy, user_settings, shared_settings); 337} 338 339} // namespace onc 340} // namespace chromeos 341