Some additional network settings cleanup
[chromium-blink-merge.git] / chromeos / network / onc / onc_merger.cc
blob4dcaae98d0e0d6146ad826cf5ee55ea2a020dc41
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.
5 #include "chromeos/network/onc/onc_merger.h"
7 #include <set>
8 #include <string>
9 #include <vector>
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"
17 namespace chromeos {
18 namespace onc {
19 namespace {
21 typedef scoped_ptr<base::DictionaryValue> DictionaryPtr;
23 // Returns true if the field is the identifier of a configuration, i.e. the GUID
24 // of a network or a certificate. These can be special handled during merging
25 // because they are always identical for the various setting sources.
26 bool IsIdentifierField(const OncValueSignature& value_signature,
27 const std::string& field_name) {
28 if (&value_signature == &kNetworkConfigurationSignature)
29 return field_name == ::onc::network_config::kGUID;
30 if (&value_signature == &kCertificateSignature)
31 return field_name == ::onc::certificate::kGUID;
32 return false;
35 // Inserts |true| at every field name in |result| that is recommended in
36 // |policy|.
37 void MarkRecommendedFieldnames(const base::DictionaryValue& policy,
38 base::DictionaryValue* result) {
39 const base::ListValue* recommended_value = NULL;
40 if (!policy.GetListWithoutPathExpansion(::onc::kRecommended,
41 &recommended_value))
42 return;
43 for (base::ListValue::const_iterator it = recommended_value->begin();
44 it != recommended_value->end(); ++it) {
45 std::string entry;
46 if ((*it)->GetAsString(&entry))
47 result->SetBooleanWithoutPathExpansion(entry, true);
51 // Returns a dictionary which contains |true| at each path that is editable by
52 // the user. No other fields are set.
53 DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) {
54 DictionaryPtr result_editable(new base::DictionaryValue);
55 MarkRecommendedFieldnames(policy, result_editable.get());
57 // Recurse into nested dictionaries.
58 for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd();
59 it.Advance()) {
60 const base::DictionaryValue* child_policy = NULL;
61 if (it.key() == ::onc::kRecommended ||
62 !it.value().GetAsDictionary(&child_policy)) {
63 continue;
66 result_editable->SetWithoutPathExpansion(
67 it.key(), GetEditableFlags(*child_policy).release());
69 return result_editable.Pass();
72 // This is the base class for merging a list of DictionaryValues in
73 // parallel. See MergeDictionaries function.
74 class MergeListOfDictionaries {
75 public:
76 typedef std::vector<const base::DictionaryValue*> DictPtrs;
78 MergeListOfDictionaries() {
81 virtual ~MergeListOfDictionaries() {
84 // For each path in any of the dictionaries |dicts|, the function
85 // MergeListOfValues is called with the list of values that are located at
86 // that path in each of the dictionaries. This function returns a new
87 // dictionary containing all results of MergeListOfValues at the respective
88 // paths. The resulting dictionary doesn't contain empty dictionaries.
89 DictionaryPtr MergeDictionaries(const DictPtrs &dicts) {
90 DictionaryPtr result(new base::DictionaryValue);
91 std::set<std::string> visited;
92 for (DictPtrs::const_iterator it_outer = dicts.begin();
93 it_outer != dicts.end(); ++it_outer) {
94 if (!*it_outer)
95 continue;
97 for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd();
98 field.Advance()) {
99 const std::string& key = field.key();
100 if (key == ::onc::kRecommended || !visited.insert(key).second)
101 continue;
103 scoped_ptr<base::Value> merged_value;
104 if (field.value().IsType(base::Value::TYPE_DICTIONARY)) {
105 DictPtrs nested_dicts;
106 for (DictPtrs::const_iterator it_inner = dicts.begin();
107 it_inner != dicts.end(); ++it_inner) {
108 const base::DictionaryValue* nested_dict = NULL;
109 if (*it_inner)
110 (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict);
111 nested_dicts.push_back(nested_dict);
113 DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts));
114 if (!merged_dict->empty())
115 merged_value = merged_dict.Pass();
116 } else {
117 std::vector<const base::Value*> values;
118 for (DictPtrs::const_iterator it_inner = dicts.begin();
119 it_inner != dicts.end(); ++it_inner) {
120 const base::Value* value = NULL;
121 if (*it_inner)
122 (*it_inner)->GetWithoutPathExpansion(key, &value);
123 values.push_back(value);
125 merged_value = MergeListOfValues(key, values);
128 if (merged_value)
129 result->SetWithoutPathExpansion(key, merged_value.release());
132 return result.Pass();
135 protected:
136 // This function is called by MergeDictionaries for each list of values that
137 // are located at the same path in each of the dictionaries. The order of the
138 // values is the same as of the given dictionaries |dicts|. If a dictionary
139 // doesn't contain a path then it's value is NULL.
140 virtual scoped_ptr<base::Value> MergeListOfValues(
141 const std::string& key,
142 const std::vector<const base::Value*>& values) = 0;
144 virtual DictionaryPtr MergeNestedDictionaries(const std::string& key,
145 const DictPtrs &dicts) {
146 return MergeDictionaries(dicts);
149 private:
150 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries);
153 // This is the base class for merging policies and user settings.
154 class MergeSettingsAndPolicies : public MergeListOfDictionaries {
155 public:
156 struct ValueParams {
157 const base::Value* user_policy;
158 const base::Value* device_policy;
159 const base::Value* user_setting;
160 const base::Value* shared_setting;
161 const base::Value* active_setting;
162 bool user_editable;
163 bool device_editable;
166 MergeSettingsAndPolicies() {}
168 // Merge the provided dictionaries. For each path in any of the dictionaries,
169 // MergeValues is called. Its results are collected in a new dictionary which
170 // is then returned. The resulting dictionary never contains empty
171 // dictionaries.
172 DictionaryPtr MergeDictionaries(
173 const base::DictionaryValue* user_policy,
174 const base::DictionaryValue* device_policy,
175 const base::DictionaryValue* user_settings,
176 const base::DictionaryValue* shared_settings,
177 const base::DictionaryValue* active_settings) {
178 hasUserPolicy_ = (user_policy != NULL);
179 hasDevicePolicy_ = (device_policy != NULL);
181 DictionaryPtr user_editable;
182 if (user_policy != NULL)
183 user_editable = GetEditableFlags(*user_policy);
185 DictionaryPtr device_editable;
186 if (device_policy != NULL)
187 device_editable = GetEditableFlags(*device_policy);
189 std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL);
190 dicts[kUserPolicyIndex] = user_policy;
191 dicts[kDevicePolicyIndex] = device_policy;
192 dicts[kUserSettingsIndex] = user_settings;
193 dicts[kSharedSettingsIndex] = shared_settings;
194 dicts[kActiveSettingsIndex] = active_settings;
195 dicts[kUserEditableIndex] = user_editable.get();
196 dicts[kDeviceEditableIndex] = device_editable.get();
197 return MergeListOfDictionaries::MergeDictionaries(dicts);
200 protected:
201 // This function is called by MergeDictionaries for each list of values that
202 // are located at the same path in each of the dictionaries. Implementations
203 // can use the Has*Policy functions.
204 virtual scoped_ptr<base::Value> MergeValues(const std::string& key,
205 const ValueParams& values) = 0;
207 // Whether a user policy was provided.
208 bool HasUserPolicy() {
209 return hasUserPolicy_;
212 // Whether a device policy was provided.
213 bool HasDevicePolicy() {
214 return hasDevicePolicy_;
217 // MergeListOfDictionaries override.
218 virtual scoped_ptr<base::Value> MergeListOfValues(
219 const std::string& key,
220 const std::vector<const base::Value*>& values) OVERRIDE {
221 bool user_editable = !HasUserPolicy();
222 if (values[kUserEditableIndex])
223 values[kUserEditableIndex]->GetAsBoolean(&user_editable);
225 bool device_editable = !HasDevicePolicy();
226 if (values[kDeviceEditableIndex])
227 values[kDeviceEditableIndex]->GetAsBoolean(&device_editable);
229 ValueParams params;
230 params.user_policy = values[kUserPolicyIndex];
231 params.device_policy = values[kDevicePolicyIndex];
232 params.user_setting = values[kUserSettingsIndex];
233 params.shared_setting = values[kSharedSettingsIndex];
234 params.active_setting = values[kActiveSettingsIndex];
235 params.user_editable = user_editable;
236 params.device_editable = device_editable;
237 return MergeValues(key, params);
240 private:
241 enum {
242 kUserPolicyIndex,
243 kDevicePolicyIndex,
244 kUserSettingsIndex,
245 kSharedSettingsIndex,
246 kActiveSettingsIndex,
247 kUserEditableIndex,
248 kDeviceEditableIndex,
249 kLastIndex
252 bool hasUserPolicy_, hasDevicePolicy_;
254 DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies);
257 // Call MergeDictionaries to merge policies and settings to the effective
258 // values. This ignores the active settings of Shill. See the description of
259 // MergeSettingsAndPoliciesToEffective.
260 class MergeToEffective : public MergeSettingsAndPolicies {
261 public:
262 MergeToEffective() {}
264 protected:
265 // Merges |values| to the effective value (Mandatory policy overwrites user
266 // settings overwrites shared settings overwrites recommended policy). |which|
267 // is set to the respective onc::kAugmentation* constant that indicates which
268 // source of settings is effective. Note that this function may return a NULL
269 // pointer and set |which| to ::onc::kAugmentationUserPolicy, which means that
270 // the
271 // user policy didn't set a value but also didn't recommend it, thus enforcing
272 // the empty value.
273 scoped_ptr<base::Value> MergeValues(const std::string& key,
274 const ValueParams& values,
275 std::string* which) {
276 const base::Value* result = NULL;
277 which->clear();
278 if (!values.user_editable) {
279 result = values.user_policy;
280 *which = ::onc::kAugmentationUserPolicy;
281 } else if (!values.device_editable) {
282 result = values.device_policy;
283 *which = ::onc::kAugmentationDevicePolicy;
284 } else if (values.user_setting) {
285 result = values.user_setting;
286 *which = ::onc::kAugmentationUserSetting;
287 } else if (values.shared_setting) {
288 result = values.shared_setting;
289 *which = ::onc::kAugmentationSharedSetting;
290 } else if (values.user_policy) {
291 result = values.user_policy;
292 *which = ::onc::kAugmentationUserPolicy;
293 } else if (values.device_policy) {
294 result = values.device_policy;
295 *which = ::onc::kAugmentationDevicePolicy;
296 } else {
297 // Can be reached if the current field is recommended, but none of the
298 // dictionaries contained a value for it.
300 if (result)
301 return make_scoped_ptr(result->DeepCopy());
302 return scoped_ptr<base::Value>();
305 // MergeSettingsAndPolicies override.
306 virtual scoped_ptr<base::Value> MergeValues(
307 const std::string& key,
308 const ValueParams& values) OVERRIDE {
309 std::string which;
310 return MergeValues(key, values, &which);
313 private:
314 DISALLOW_COPY_AND_ASSIGN(MergeToEffective);
317 namespace {
319 // Returns true if all not-null values in |values| are equal to |value|.
320 bool AllPresentValuesEqual(const MergeSettingsAndPolicies::ValueParams& values,
321 const base::Value& value) {
322 if (values.user_policy && !value.Equals(values.user_policy))
323 return false;
324 if (values.device_policy && !value.Equals(values.device_policy))
325 return false;
326 if (values.user_setting && !value.Equals(values.user_setting))
327 return false;
328 if (values.shared_setting && !value.Equals(values.shared_setting))
329 return false;
330 if (values.active_setting && !value.Equals(values.active_setting))
331 return false;
332 return true;
335 } // namespace
337 // Call MergeDictionaries to merge policies and settings to an augmented
338 // dictionary which contains a dictionary for each value in the original
339 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
340 class MergeToAugmented : public MergeToEffective {
341 public:
342 MergeToAugmented() {}
344 DictionaryPtr MergeDictionaries(
345 const OncValueSignature& signature,
346 const base::DictionaryValue* user_policy,
347 const base::DictionaryValue* device_policy,
348 const base::DictionaryValue* user_settings,
349 const base::DictionaryValue* shared_settings,
350 const base::DictionaryValue* active_settings) {
351 signature_ = &signature;
352 return MergeToEffective::MergeDictionaries(user_policy,
353 device_policy,
354 user_settings,
355 shared_settings,
356 active_settings);
359 protected:
360 // MergeSettingsAndPolicies override.
361 virtual scoped_ptr<base::Value> MergeValues(
362 const std::string& key,
363 const ValueParams& values) OVERRIDE {
364 const OncFieldSignature* field = NULL;
365 if (signature_)
366 field = GetFieldSignature(*signature_, key);
368 if (!field) {
369 // This field is not part of the provided ONCSignature, thus it cannot be
370 // controlled by policy. Return the plain active value instead of an
371 // augmented dictionary.
372 return make_scoped_ptr(values.active_setting->DeepCopy());
375 // This field is part of the provided ONCSignature, thus it can be
376 // controlled by policy.
377 std::string which_effective;
378 scoped_ptr<base::Value> effective_value =
379 MergeToEffective::MergeValues(key, values, &which_effective);
381 if (IsIdentifierField(*signature_, key)) {
382 // Don't augment the GUID but write the plain value.
383 if (!effective_value) {
384 LOG(ERROR) << "GUID field has no effective value";
385 return make_scoped_ptr<base::Value>(NULL);
388 // DCHECK that all provided GUIDs are identical.
389 DCHECK(AllPresentValuesEqual(values, *effective_value));
391 // Return the un-augmented GUID.
392 return effective_value.Pass();
395 scoped_ptr<base::DictionaryValue> augmented_value(
396 new base::DictionaryValue);
398 if (values.active_setting) {
399 augmented_value->SetWithoutPathExpansion(
400 ::onc::kAugmentationActiveSetting, values.active_setting->DeepCopy());
403 if (!which_effective.empty()) {
404 augmented_value->SetStringWithoutPathExpansion(
405 ::onc::kAugmentationEffectiveSetting, which_effective);
408 // Prevent credentials from being forwarded in cleartext to
409 // UI. User/shared credentials are not stored separately, so they cannot
410 // leak here.
411 bool is_credential = onc::FieldIsCredential(*signature_, key);
412 if (!is_credential) {
413 if (values.user_policy) {
414 augmented_value->SetWithoutPathExpansion(
415 ::onc::kAugmentationUserPolicy, values.user_policy->DeepCopy());
417 if (values.device_policy) {
418 augmented_value->SetWithoutPathExpansion(
419 ::onc::kAugmentationDevicePolicy,
420 values.device_policy->DeepCopy());
423 if (values.user_setting) {
424 augmented_value->SetWithoutPathExpansion(
425 ::onc::kAugmentationUserSetting, values.user_setting->DeepCopy());
427 if (values.shared_setting) {
428 augmented_value->SetWithoutPathExpansion(
429 ::onc::kAugmentationSharedSetting,
430 values.shared_setting->DeepCopy());
432 if (HasUserPolicy() && values.user_editable) {
433 augmented_value->SetBooleanWithoutPathExpansion(
434 ::onc::kAugmentationUserEditable, true);
436 if (HasDevicePolicy() && values.device_editable) {
437 augmented_value->SetBooleanWithoutPathExpansion(
438 ::onc::kAugmentationDeviceEditable, true);
440 if (augmented_value->empty())
441 augmented_value.reset();
442 return augmented_value.PassAs<base::Value>();
445 // MergeListOfDictionaries override.
446 virtual DictionaryPtr MergeNestedDictionaries(
447 const std::string& key,
448 const DictPtrs &dicts) OVERRIDE {
449 DictionaryPtr result;
450 if (signature_) {
451 const OncValueSignature* enclosing_signature = signature_;
452 signature_ = NULL;
454 const OncFieldSignature* field =
455 GetFieldSignature(*enclosing_signature, key);
456 if (field)
457 signature_ = field->value_signature;
458 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
460 signature_ = enclosing_signature;
461 } else {
462 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
464 return result.Pass();
467 private:
468 const OncValueSignature* signature_;
469 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented);
472 } // namespace
474 DictionaryPtr MergeSettingsAndPoliciesToEffective(
475 const base::DictionaryValue* user_policy,
476 const base::DictionaryValue* device_policy,
477 const base::DictionaryValue* user_settings,
478 const base::DictionaryValue* shared_settings) {
479 MergeToEffective merger;
480 return merger.MergeDictionaries(
481 user_policy, device_policy, user_settings, shared_settings, NULL);
484 DictionaryPtr MergeSettingsAndPoliciesToAugmented(
485 const OncValueSignature& signature,
486 const base::DictionaryValue* user_policy,
487 const base::DictionaryValue* device_policy,
488 const base::DictionaryValue* user_settings,
489 const base::DictionaryValue* shared_settings,
490 const base::DictionaryValue* active_settings) {
491 MergeToAugmented merger;
492 return merger.MergeDictionaries(
493 signature, user_policy, device_policy, user_settings, shared_settings,
494 active_settings);
497 } // namespace onc
498 } // namespace chromeos