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 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
);
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 virtual scoped_ptr
<base::Value
> MergeValues(
307 const std::string
& key
,
308 const ValueParams
& values
) OVERRIDE
{
310 return MergeValues(key
, values
, &which
);
314 DISALLOW_COPY_AND_ASSIGN(MergeToEffective
);
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
))
324 if (values
.device_policy
&& !value
.Equals(values
.device_policy
))
326 if (values
.user_setting
&& !value
.Equals(values
.user_setting
))
328 if (values
.shared_setting
&& !value
.Equals(values
.shared_setting
))
330 if (values
.active_setting
&& !value
.Equals(values
.active_setting
))
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
{
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
,
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
;
366 field
= GetFieldSignature(*signature_
, key
);
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
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
;
451 const OncValueSignature
* enclosing_signature
= signature_
;
454 const OncFieldSignature
* field
=
455 GetFieldSignature(*enclosing_signature
, key
);
457 signature_
= field
->value_signature
;
458 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
460 signature_
= enclosing_signature
;
462 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
464 return result
.Pass();
468 const OncValueSignature
* signature_
;
469 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented
);
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
,
498 } // namespace chromeos