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.
25 bool IsIdentifierField(const OncValueSignature
& value_signature
,
26 const std::string
& field_name
) {
27 if (&value_signature
== &kNetworkConfigurationSignature
)
28 return field_name
== ::onc::network_config::kGUID
;
29 if (&value_signature
== &kCertificateSignature
)
30 return field_name
== ::onc::certificate::kGUID
;
34 // Identifier fields and other read-only fields (specifically Type) are
35 // handled specially during merging because they are always identical for the
36 // various setting sources.
37 bool IsReadOnlyField(const OncValueSignature
& value_signature
,
38 const std::string
& field_name
) {
39 if (IsIdentifierField(value_signature
, field_name
))
41 if (&value_signature
== &kNetworkConfigurationSignature
)
42 return field_name
== ::onc::network_config::kType
;
46 // Inserts |true| at every field name in |result| that is recommended in
48 void MarkRecommendedFieldnames(const base::DictionaryValue
& policy
,
49 base::DictionaryValue
* result
) {
50 const base::ListValue
* recommended_value
= NULL
;
51 if (!policy
.GetListWithoutPathExpansion(::onc::kRecommended
,
54 for (base::ListValue::const_iterator it
= recommended_value
->begin();
55 it
!= recommended_value
->end(); ++it
) {
57 if ((*it
)->GetAsString(&entry
))
58 result
->SetBooleanWithoutPathExpansion(entry
, true);
62 // Returns a dictionary which contains |true| at each path that is editable by
63 // the user. No other fields are set.
64 DictionaryPtr
GetEditableFlags(const base::DictionaryValue
& policy
) {
65 DictionaryPtr
result_editable(new base::DictionaryValue
);
66 MarkRecommendedFieldnames(policy
, result_editable
.get());
68 // Recurse into nested dictionaries.
69 for (base::DictionaryValue::Iterator
it(policy
); !it
.IsAtEnd();
71 const base::DictionaryValue
* child_policy
= NULL
;
72 if (it
.key() == ::onc::kRecommended
||
73 !it
.value().GetAsDictionary(&child_policy
)) {
77 result_editable
->SetWithoutPathExpansion(
78 it
.key(), GetEditableFlags(*child_policy
).release());
80 return result_editable
.Pass();
83 // This is the base class for merging a list of DictionaryValues in
84 // parallel. See MergeDictionaries function.
85 class MergeListOfDictionaries
{
87 typedef std::vector
<const base::DictionaryValue
*> DictPtrs
;
89 MergeListOfDictionaries() {
92 virtual ~MergeListOfDictionaries() {
95 // For each path in any of the dictionaries |dicts|, the function
96 // MergeListOfValues is called with the list of values that are located at
97 // that path in each of the dictionaries. This function returns a new
98 // dictionary containing all results of MergeListOfValues at the respective
99 // paths. The resulting dictionary doesn't contain empty dictionaries.
100 DictionaryPtr
MergeDictionaries(const DictPtrs
&dicts
) {
101 DictionaryPtr
result(new base::DictionaryValue
);
102 std::set
<std::string
> visited
;
103 for (DictPtrs::const_iterator it_outer
= dicts
.begin();
104 it_outer
!= dicts
.end(); ++it_outer
) {
108 for (base::DictionaryValue::Iterator
field(**it_outer
); !field
.IsAtEnd();
110 const std::string
& key
= field
.key();
111 if (key
== ::onc::kRecommended
|| !visited
.insert(key
).second
)
114 scoped_ptr
<base::Value
> merged_value
;
115 if (field
.value().IsType(base::Value::TYPE_DICTIONARY
)) {
116 DictPtrs nested_dicts
;
117 for (DictPtrs::const_iterator it_inner
= dicts
.begin();
118 it_inner
!= dicts
.end(); ++it_inner
) {
119 const base::DictionaryValue
* nested_dict
= NULL
;
121 (*it_inner
)->GetDictionaryWithoutPathExpansion(key
, &nested_dict
);
122 nested_dicts
.push_back(nested_dict
);
124 DictionaryPtr
merged_dict(MergeNestedDictionaries(key
, nested_dicts
));
125 if (!merged_dict
->empty())
126 merged_value
= merged_dict
.Pass();
128 std::vector
<const base::Value
*> values
;
129 for (DictPtrs::const_iterator it_inner
= dicts
.begin();
130 it_inner
!= dicts
.end(); ++it_inner
) {
131 const base::Value
* value
= NULL
;
133 (*it_inner
)->GetWithoutPathExpansion(key
, &value
);
134 values
.push_back(value
);
136 merged_value
= MergeListOfValues(key
, values
);
140 result
->SetWithoutPathExpansion(key
, merged_value
.release());
143 return result
.Pass();
147 // This function is called by MergeDictionaries for each list of values that
148 // are located at the same path in each of the dictionaries. The order of the
149 // values is the same as of the given dictionaries |dicts|. If a dictionary
150 // doesn't contain a path then it's value is NULL.
151 virtual scoped_ptr
<base::Value
> MergeListOfValues(
152 const std::string
& key
,
153 const std::vector
<const base::Value
*>& values
) = 0;
155 virtual DictionaryPtr
MergeNestedDictionaries(const std::string
& key
,
156 const DictPtrs
&dicts
) {
157 return MergeDictionaries(dicts
);
161 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries
);
164 // This is the base class for merging policies and user settings.
165 class MergeSettingsAndPolicies
: public MergeListOfDictionaries
{
168 const base::Value
* user_policy
;
169 const base::Value
* device_policy
;
170 const base::Value
* user_setting
;
171 const base::Value
* shared_setting
;
172 const base::Value
* active_setting
;
174 bool device_editable
;
177 MergeSettingsAndPolicies() {}
179 // Merge the provided dictionaries. For each path in any of the dictionaries,
180 // MergeValues is called. Its results are collected in a new dictionary which
181 // is then returned. The resulting dictionary never contains empty
183 DictionaryPtr
MergeDictionaries(
184 const base::DictionaryValue
* user_policy
,
185 const base::DictionaryValue
* device_policy
,
186 const base::DictionaryValue
* user_settings
,
187 const base::DictionaryValue
* shared_settings
,
188 const base::DictionaryValue
* active_settings
) {
189 hasUserPolicy_
= (user_policy
!= NULL
);
190 hasDevicePolicy_
= (device_policy
!= NULL
);
192 DictionaryPtr user_editable
;
193 if (user_policy
!= NULL
)
194 user_editable
= GetEditableFlags(*user_policy
);
196 DictionaryPtr device_editable
;
197 if (device_policy
!= NULL
)
198 device_editable
= GetEditableFlags(*device_policy
);
200 std::vector
<const base::DictionaryValue
*> dicts(kLastIndex
, NULL
);
201 dicts
[kUserPolicyIndex
] = user_policy
;
202 dicts
[kDevicePolicyIndex
] = device_policy
;
203 dicts
[kUserSettingsIndex
] = user_settings
;
204 dicts
[kSharedSettingsIndex
] = shared_settings
;
205 dicts
[kActiveSettingsIndex
] = active_settings
;
206 dicts
[kUserEditableIndex
] = user_editable
.get();
207 dicts
[kDeviceEditableIndex
] = device_editable
.get();
208 return MergeListOfDictionaries::MergeDictionaries(dicts
);
212 // This function is called by MergeDictionaries for each list of values that
213 // are located at the same path in each of the dictionaries. Implementations
214 // can use the Has*Policy functions.
215 virtual scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
216 const ValueParams
& values
) = 0;
218 // Whether a user policy was provided.
219 bool HasUserPolicy() {
220 return hasUserPolicy_
;
223 // Whether a device policy was provided.
224 bool HasDevicePolicy() {
225 return hasDevicePolicy_
;
228 // MergeListOfDictionaries override.
229 scoped_ptr
<base::Value
> MergeListOfValues(
230 const std::string
& key
,
231 const std::vector
<const base::Value
*>& values
) override
{
232 bool user_editable
= !HasUserPolicy();
233 if (values
[kUserEditableIndex
])
234 values
[kUserEditableIndex
]->GetAsBoolean(&user_editable
);
236 bool device_editable
= !HasDevicePolicy();
237 if (values
[kDeviceEditableIndex
])
238 values
[kDeviceEditableIndex
]->GetAsBoolean(&device_editable
);
241 params
.user_policy
= values
[kUserPolicyIndex
];
242 params
.device_policy
= values
[kDevicePolicyIndex
];
243 params
.user_setting
= values
[kUserSettingsIndex
];
244 params
.shared_setting
= values
[kSharedSettingsIndex
];
245 params
.active_setting
= values
[kActiveSettingsIndex
];
246 params
.user_editable
= user_editable
;
247 params
.device_editable
= device_editable
;
248 return MergeValues(key
, params
);
256 kSharedSettingsIndex
,
257 kActiveSettingsIndex
,
259 kDeviceEditableIndex
,
263 bool hasUserPolicy_
, hasDevicePolicy_
;
265 DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies
);
268 // Call MergeDictionaries to merge policies and settings to the effective
269 // values. This ignores the active settings of Shill. See the description of
270 // MergeSettingsAndPoliciesToEffective.
271 class MergeToEffective
: public MergeSettingsAndPolicies
{
273 MergeToEffective() {}
276 // Merges |values| to the effective value (Mandatory policy overwrites user
277 // settings overwrites shared settings overwrites recommended policy). |which|
278 // is set to the respective onc::kAugmentation* constant that indicates which
279 // source of settings is effective. Note that this function may return a NULL
280 // pointer and set |which| to ::onc::kAugmentationUserPolicy, which means that
282 // user policy didn't set a value but also didn't recommend it, thus enforcing
284 scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
285 const ValueParams
& values
,
286 std::string
* which
) {
287 const base::Value
* result
= NULL
;
289 if (!values
.user_editable
) {
290 result
= values
.user_policy
;
291 *which
= ::onc::kAugmentationUserPolicy
;
292 } else if (!values
.device_editable
) {
293 result
= values
.device_policy
;
294 *which
= ::onc::kAugmentationDevicePolicy
;
295 } else if (values
.user_setting
) {
296 result
= values
.user_setting
;
297 *which
= ::onc::kAugmentationUserSetting
;
298 } else if (values
.shared_setting
) {
299 result
= values
.shared_setting
;
300 *which
= ::onc::kAugmentationSharedSetting
;
301 } else if (values
.user_policy
) {
302 result
= values
.user_policy
;
303 *which
= ::onc::kAugmentationUserPolicy
;
304 } else if (values
.device_policy
) {
305 result
= values
.device_policy
;
306 *which
= ::onc::kAugmentationDevicePolicy
;
308 // Can be reached if the current field is recommended, but none of the
309 // dictionaries contained a value for it.
312 return make_scoped_ptr(result
->DeepCopy());
313 return scoped_ptr
<base::Value
>();
316 // MergeSettingsAndPolicies override.
317 scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
318 const ValueParams
& values
) override
{
320 return MergeValues(key
, values
, &which
);
324 DISALLOW_COPY_AND_ASSIGN(MergeToEffective
);
329 // Returns true if all not-null values in |values| are equal to |value|.
330 bool AllPresentValuesEqual(const MergeSettingsAndPolicies::ValueParams
& values
,
331 const base::Value
& value
) {
332 if (values
.user_policy
&& !value
.Equals(values
.user_policy
))
334 if (values
.device_policy
&& !value
.Equals(values
.device_policy
))
336 if (values
.user_setting
&& !value
.Equals(values
.user_setting
))
338 if (values
.shared_setting
&& !value
.Equals(values
.shared_setting
))
340 if (values
.active_setting
&& !value
.Equals(values
.active_setting
))
347 // Call MergeDictionaries to merge policies and settings to an augmented
348 // dictionary which contains a dictionary for each value in the original
349 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
350 class MergeToAugmented
: public MergeToEffective
{
352 MergeToAugmented() {}
354 DictionaryPtr
MergeDictionaries(
355 const OncValueSignature
& signature
,
356 const base::DictionaryValue
* user_policy
,
357 const base::DictionaryValue
* device_policy
,
358 const base::DictionaryValue
* user_settings
,
359 const base::DictionaryValue
* shared_settings
,
360 const base::DictionaryValue
* active_settings
) {
361 signature_
= &signature
;
362 return MergeToEffective::MergeDictionaries(user_policy
,
370 // MergeSettingsAndPolicies override.
371 scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
372 const ValueParams
& values
) override
{
373 const OncFieldSignature
* field
= NULL
;
375 field
= GetFieldSignature(*signature_
, key
);
378 // This field is not part of the provided ONCSignature, thus it cannot be
379 // controlled by policy. Return the plain active value instead of an
380 // augmented dictionary.
381 if (values
.active_setting
)
382 return make_scoped_ptr(values
.active_setting
->DeepCopy());
386 // This field is part of the provided ONCSignature, thus it can be
387 // controlled by policy.
388 std::string which_effective
;
389 scoped_ptr
<base::Value
> effective_value
=
390 MergeToEffective::MergeValues(key
, values
, &which_effective
);
392 if (IsReadOnlyField(*signature_
, key
)) {
393 // Don't augment read-only fields (GUID and Type).
394 if (effective_value
) {
395 // DCHECK that all provided fields are identical.
396 DCHECK(AllPresentValuesEqual(values
, *effective_value
))
397 << "Values do not match: " << key
398 << " Effective: " << *effective_value
;
399 // Return the un-augmented field.
400 return effective_value
.Pass();
402 if (values
.active_setting
) {
403 // Unmanaged networks have assigned (active) values.
404 return make_scoped_ptr(values
.active_setting
->DeepCopy());
406 LOG(ERROR
) << "Field has no effective value: " << key
;
410 scoped_ptr
<base::DictionaryValue
> augmented_value(
411 new base::DictionaryValue
);
413 if (values
.active_setting
) {
414 augmented_value
->SetWithoutPathExpansion(
415 ::onc::kAugmentationActiveSetting
, values
.active_setting
->DeepCopy());
418 if (!which_effective
.empty()) {
419 augmented_value
->SetStringWithoutPathExpansion(
420 ::onc::kAugmentationEffectiveSetting
, which_effective
);
423 // Prevent credentials from being forwarded in cleartext to
424 // UI. User/shared credentials are not stored separately, so they cannot
426 bool is_credential
= onc::FieldIsCredential(*signature_
, key
);
427 if (!is_credential
) {
428 if (values
.user_policy
) {
429 augmented_value
->SetWithoutPathExpansion(
430 ::onc::kAugmentationUserPolicy
, values
.user_policy
->DeepCopy());
432 if (values
.device_policy
) {
433 augmented_value
->SetWithoutPathExpansion(
434 ::onc::kAugmentationDevicePolicy
,
435 values
.device_policy
->DeepCopy());
438 if (values
.user_setting
) {
439 augmented_value
->SetWithoutPathExpansion(
440 ::onc::kAugmentationUserSetting
, values
.user_setting
->DeepCopy());
442 if (values
.shared_setting
) {
443 augmented_value
->SetWithoutPathExpansion(
444 ::onc::kAugmentationSharedSetting
,
445 values
.shared_setting
->DeepCopy());
447 if (HasUserPolicy() && values
.user_editable
) {
448 augmented_value
->SetBooleanWithoutPathExpansion(
449 ::onc::kAugmentationUserEditable
, true);
451 if (HasDevicePolicy() && values
.device_editable
) {
452 augmented_value
->SetBooleanWithoutPathExpansion(
453 ::onc::kAugmentationDeviceEditable
, true);
455 if (augmented_value
->empty())
456 augmented_value
.reset();
457 return augmented_value
.Pass();
460 // MergeListOfDictionaries override.
461 DictionaryPtr
MergeNestedDictionaries(const std::string
& key
,
462 const DictPtrs
& dicts
) override
{
463 DictionaryPtr result
;
465 const OncValueSignature
* enclosing_signature
= signature_
;
468 const OncFieldSignature
* field
=
469 GetFieldSignature(*enclosing_signature
, key
);
471 signature_
= field
->value_signature
;
472 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
474 signature_
= enclosing_signature
;
476 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
478 return result
.Pass();
482 const OncValueSignature
* signature_
;
483 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented
);
488 DictionaryPtr
MergeSettingsAndPoliciesToEffective(
489 const base::DictionaryValue
* user_policy
,
490 const base::DictionaryValue
* device_policy
,
491 const base::DictionaryValue
* user_settings
,
492 const base::DictionaryValue
* shared_settings
) {
493 MergeToEffective merger
;
494 return merger
.MergeDictionaries(
495 user_policy
, device_policy
, user_settings
, shared_settings
, NULL
);
498 DictionaryPtr
MergeSettingsAndPoliciesToAugmented(
499 const OncValueSignature
& signature
,
500 const base::DictionaryValue
* user_policy
,
501 const base::DictionaryValue
* device_policy
,
502 const base::DictionaryValue
* user_settings
,
503 const base::DictionaryValue
* shared_settings
,
504 const base::DictionaryValue
* active_settings
) {
505 MergeToAugmented merger
;
506 return merger
.MergeDictionaries(
507 signature
, user_policy
, device_policy
, user_settings
, shared_settings
,
512 } // namespace chromeos