Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chromeos / network / onc / onc_merger.cc
blob931c3f0a9ea187126672b70a74cffd267b61f569
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 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 scoped_ptr<base::Value> MergeValues(const std::string& key,
307 const ValueParams& values) override {
308 std::string which;
309 return MergeValues(key, values, &which);
312 private:
313 DISALLOW_COPY_AND_ASSIGN(MergeToEffective);
316 namespace {
318 // Returns true if all not-null values in |values| are equal to |value|.
319 bool AllPresentValuesEqual(const MergeSettingsAndPolicies::ValueParams& values,
320 const base::Value& value) {
321 if (values.user_policy && !value.Equals(values.user_policy))
322 return false;
323 if (values.device_policy && !value.Equals(values.device_policy))
324 return false;
325 if (values.user_setting && !value.Equals(values.user_setting))
326 return false;
327 if (values.shared_setting && !value.Equals(values.shared_setting))
328 return false;
329 if (values.active_setting && !value.Equals(values.active_setting))
330 return false;
331 return true;
334 } // namespace
336 // Call MergeDictionaries to merge policies and settings to an augmented
337 // dictionary which contains a dictionary for each value in the original
338 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
339 class MergeToAugmented : public MergeToEffective {
340 public:
341 MergeToAugmented() {}
343 DictionaryPtr MergeDictionaries(
344 const OncValueSignature& signature,
345 const base::DictionaryValue* user_policy,
346 const base::DictionaryValue* device_policy,
347 const base::DictionaryValue* user_settings,
348 const base::DictionaryValue* shared_settings,
349 const base::DictionaryValue* active_settings) {
350 signature_ = &signature;
351 return MergeToEffective::MergeDictionaries(user_policy,
352 device_policy,
353 user_settings,
354 shared_settings,
355 active_settings);
358 protected:
359 // MergeSettingsAndPolicies override.
360 scoped_ptr<base::Value> MergeValues(const std::string& key,
361 const ValueParams& values) override {
362 const OncFieldSignature* field = NULL;
363 if (signature_)
364 field = GetFieldSignature(*signature_, key);
366 if (!field) {
367 // This field is not part of the provided ONCSignature, thus it cannot be
368 // controlled by policy. Return the plain active value instead of an
369 // augmented dictionary.
370 if (values.active_setting)
371 return make_scoped_ptr(values.active_setting->DeepCopy());
372 return nullptr;
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 // DCHECK that all provided GUIDs are identical.
385 DCHECK(AllPresentValuesEqual(values, *effective_value));
386 // Return the un-augmented GUID.
387 return effective_value.Pass();
389 if (values.active_setting) {
390 // Unmanaged networks have assigned (active) GUID values.
391 return make_scoped_ptr(values.active_setting->DeepCopy());
393 LOG(ERROR) << "GUID field has no effective value";
394 return nullptr;
397 scoped_ptr<base::DictionaryValue> augmented_value(
398 new base::DictionaryValue);
400 if (values.active_setting) {
401 augmented_value->SetWithoutPathExpansion(
402 ::onc::kAugmentationActiveSetting, values.active_setting->DeepCopy());
405 if (!which_effective.empty()) {
406 augmented_value->SetStringWithoutPathExpansion(
407 ::onc::kAugmentationEffectiveSetting, which_effective);
410 // Prevent credentials from being forwarded in cleartext to
411 // UI. User/shared credentials are not stored separately, so they cannot
412 // leak here.
413 bool is_credential = onc::FieldIsCredential(*signature_, key);
414 if (!is_credential) {
415 if (values.user_policy) {
416 augmented_value->SetWithoutPathExpansion(
417 ::onc::kAugmentationUserPolicy, values.user_policy->DeepCopy());
419 if (values.device_policy) {
420 augmented_value->SetWithoutPathExpansion(
421 ::onc::kAugmentationDevicePolicy,
422 values.device_policy->DeepCopy());
425 if (values.user_setting) {
426 augmented_value->SetWithoutPathExpansion(
427 ::onc::kAugmentationUserSetting, values.user_setting->DeepCopy());
429 if (values.shared_setting) {
430 augmented_value->SetWithoutPathExpansion(
431 ::onc::kAugmentationSharedSetting,
432 values.shared_setting->DeepCopy());
434 if (HasUserPolicy() && values.user_editable) {
435 augmented_value->SetBooleanWithoutPathExpansion(
436 ::onc::kAugmentationUserEditable, true);
438 if (HasDevicePolicy() && values.device_editable) {
439 augmented_value->SetBooleanWithoutPathExpansion(
440 ::onc::kAugmentationDeviceEditable, true);
442 if (augmented_value->empty())
443 augmented_value.reset();
444 return augmented_value.Pass();
447 // MergeListOfDictionaries override.
448 DictionaryPtr MergeNestedDictionaries(const std::string& key,
449 const DictPtrs& dicts) override {
450 DictionaryPtr result;
451 if (signature_) {
452 const OncValueSignature* enclosing_signature = signature_;
453 signature_ = NULL;
455 const OncFieldSignature* field =
456 GetFieldSignature(*enclosing_signature, key);
457 if (field)
458 signature_ = field->value_signature;
459 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
461 signature_ = enclosing_signature;
462 } else {
463 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
465 return result.Pass();
468 private:
469 const OncValueSignature* signature_;
470 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented);
473 } // namespace
475 DictionaryPtr MergeSettingsAndPoliciesToEffective(
476 const base::DictionaryValue* user_policy,
477 const base::DictionaryValue* device_policy,
478 const base::DictionaryValue* user_settings,
479 const base::DictionaryValue* shared_settings) {
480 MergeToEffective merger;
481 return merger.MergeDictionaries(
482 user_policy, device_policy, user_settings, shared_settings, NULL);
485 DictionaryPtr MergeSettingsAndPoliciesToAugmented(
486 const OncValueSignature& signature,
487 const base::DictionaryValue* user_policy,
488 const base::DictionaryValue* device_policy,
489 const base::DictionaryValue* user_settings,
490 const base::DictionaryValue* shared_settings,
491 const base::DictionaryValue* active_settings) {
492 MergeToAugmented merger;
493 return merger.MergeDictionaries(
494 signature, user_policy, device_policy, user_settings, shared_settings,
495 active_settings);
498 } // namespace onc
499 } // namespace chromeos