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"
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"
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
;
35 // Inserts |true| at every field name in |result| that is recommended in
37 void MarkRecommendedFieldnames(const base::DictionaryValue
& policy
,
38 base::DictionaryValue
* result
) {
39 const base::ListValue
* recommended_value
= NULL
;
40 if (!policy
.GetListWithoutPathExpansion(::onc::kRecommended
,
43 for (base::ListValue::const_iterator it
= recommended_value
->begin();
44 it
!= recommended_value
->end(); ++it
) {
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();
60 const base::DictionaryValue
* child_policy
= NULL
;
61 if (it
.key() == ::onc::kRecommended
||
62 !it
.value().GetAsDictionary(&child_policy
)) {
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
{
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
) {
97 for (base::DictionaryValue::Iterator
field(**it_outer
); !field
.IsAtEnd();
99 const std::string
& key
= field
.key();
100 if (key
== ::onc::kRecommended
|| !visited
.insert(key
).second
)
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
;
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();
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
;
122 (*it_inner
)->GetWithoutPathExpansion(key
, &value
);
123 values
.push_back(value
);
125 merged_value
= MergeListOfValues(key
, values
);
129 result
->SetWithoutPathExpansion(key
, merged_value
.release());
132 return result
.Pass();
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
);
150 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries
);
153 // This is the base class for merging policies and user settings.
154 class MergeSettingsAndPolicies
: public MergeListOfDictionaries
{
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
;
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
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
);
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
);
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
);
245 kSharedSettingsIndex
,
246 kActiveSettingsIndex
,
248 kDeviceEditableIndex
,
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
{
262 MergeToEffective() {}
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
271 // user policy didn't set a value but also didn't recommend it, thus enforcing
273 scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
274 const ValueParams
& values
,
275 std::string
* which
) {
276 const base::Value
* result
= NULL
;
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
;
297 // Can be reached if the current field is recommended, but none of the
298 // dictionaries contained a value for it.
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
{
309 return MergeValues(key
, values
, &which
);
313 DISALLOW_COPY_AND_ASSIGN(MergeToEffective
);
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
))
323 if (values
.device_policy
&& !value
.Equals(values
.device_policy
))
325 if (values
.user_setting
&& !value
.Equals(values
.user_setting
))
327 if (values
.shared_setting
&& !value
.Equals(values
.shared_setting
))
329 if (values
.active_setting
&& !value
.Equals(values
.active_setting
))
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
{
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
,
359 // MergeSettingsAndPolicies override.
360 scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
361 const ValueParams
& values
) override
{
362 const OncFieldSignature
* field
= NULL
;
364 field
= GetFieldSignature(*signature_
, key
);
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());
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";
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
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
;
452 const OncValueSignature
* enclosing_signature
= signature_
;
455 const OncFieldSignature
* field
=
456 GetFieldSignature(*enclosing_signature
, key
);
458 signature_
= field
->value_signature
;
459 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
461 signature_
= enclosing_signature
;
463 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
465 return result
.Pass();
469 const OncValueSignature
* signature_
;
470 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented
);
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
,
499 } // namespace chromeos