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_constants.h"
15 #include "chromeos/network/onc/onc_signature.h"
21 typedef scoped_ptr
<base::DictionaryValue
> DictionaryPtr
;
23 // Inserts |true| at every field name in |result| that is recommended in
25 void MarkRecommendedFieldnames(const base::DictionaryValue
& policy
,
26 base::DictionaryValue
* result
) {
27 const base::ListValue
* recommended_value
= NULL
;
28 if (!policy
.GetListWithoutPathExpansion(kRecommended
, &recommended_value
))
30 for (base::ListValue::const_iterator it
= recommended_value
->begin();
31 it
!= recommended_value
->end(); ++it
) {
33 if ((*it
)->GetAsString(&entry
))
34 result
->SetBooleanWithoutPathExpansion(entry
, true);
38 // Returns a dictionary which contains |true| at each path that is editable by
39 // the user. No other fields are set.
40 DictionaryPtr
GetEditableFlags(const base::DictionaryValue
& policy
) {
41 DictionaryPtr
result_editable(new base::DictionaryValue
);
42 MarkRecommendedFieldnames(policy
, result_editable
.get());
44 // Recurse into nested dictionaries.
45 for (base::DictionaryValue::Iterator
it(policy
); !it
.IsAtEnd();
47 const base::DictionaryValue
* child_policy
= NULL
;
48 if (it
.key() == kRecommended
||
49 !it
.value().GetAsDictionary(&child_policy
)) {
53 result_editable
->SetWithoutPathExpansion(
54 it
.key(), GetEditableFlags(*child_policy
).release());
56 return result_editable
.Pass();
59 // This is the base class for merging a list of DictionaryValues in
60 // parallel. See MergeDictionaries function.
61 class MergeListOfDictionaries
{
63 typedef std::vector
<const base::DictionaryValue
*> DictPtrs
;
65 MergeListOfDictionaries() {
68 virtual ~MergeListOfDictionaries() {
71 // For each path in any of the dictionaries |dicts|, the function
72 // MergeListOfValues is called with the list of values that are located at
73 // that path in each of the dictionaries. This function returns a new
74 // dictionary containing all results of MergeListOfValues at the respective
75 // paths. The resulting dictionary doesn't contain empty dictionaries.
76 DictionaryPtr
MergeDictionaries(const DictPtrs
&dicts
) {
77 DictionaryPtr
result(new base::DictionaryValue
);
78 std::set
<std::string
> visited
;
79 for (DictPtrs::const_iterator it_outer
= dicts
.begin();
80 it_outer
!= dicts
.end(); ++it_outer
) {
84 for (base::DictionaryValue::Iterator
field(**it_outer
); !field
.IsAtEnd();
86 const std::string
& key
= field
.key();
87 if (key
== kRecommended
|| !visited
.insert(key
).second
)
90 scoped_ptr
<base::Value
> merged_value
;
91 if (field
.value().IsType(base::Value::TYPE_DICTIONARY
)) {
92 DictPtrs nested_dicts
;
93 for (DictPtrs::const_iterator it_inner
= dicts
.begin();
94 it_inner
!= dicts
.end(); ++it_inner
) {
95 const base::DictionaryValue
* nested_dict
= NULL
;
97 (*it_inner
)->GetDictionaryWithoutPathExpansion(key
, &nested_dict
);
98 nested_dicts
.push_back(nested_dict
);
100 DictionaryPtr
merged_dict(MergeNestedDictionaries(key
, nested_dicts
));
101 if (!merged_dict
->empty())
102 merged_value
= merged_dict
.Pass();
104 std::vector
<const base::Value
*> values
;
105 for (DictPtrs::const_iterator it_inner
= dicts
.begin();
106 it_inner
!= dicts
.end(); ++it_inner
) {
107 const base::Value
* value
= NULL
;
109 (*it_inner
)->GetWithoutPathExpansion(key
, &value
);
110 values
.push_back(value
);
112 merged_value
= MergeListOfValues(key
, values
);
116 result
->SetWithoutPathExpansion(key
, merged_value
.release());
119 return result
.Pass();
123 // This function is called by MergeDictionaries for each list of values that
124 // are located at the same path in each of the dictionaries. The order of the
125 // values is the same as of the given dictionaries |dicts|. If a dictionary
126 // doesn't contain a path then it's value is NULL.
127 virtual scoped_ptr
<base::Value
> MergeListOfValues(
128 const std::string
& key
,
129 const std::vector
<const base::Value
*>& values
) = 0;
131 virtual DictionaryPtr
MergeNestedDictionaries(const std::string
& key
,
132 const DictPtrs
&dicts
) {
133 return MergeDictionaries(dicts
);
137 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries
);
140 // This is the base class for merging policies and user settings.
141 class MergeSettingsAndPolicies
: public MergeListOfDictionaries
{
144 const base::Value
* user_policy
;
145 const base::Value
* device_policy
;
146 const base::Value
* user_setting
;
147 const base::Value
* shared_setting
;
148 const base::Value
* active_setting
;
150 bool device_editable
;
153 MergeSettingsAndPolicies() {}
155 // Merge the provided dictionaries. For each path in any of the dictionaries,
156 // MergeValues is called. Its results are collected in a new dictionary which
157 // is then returned. The resulting dictionary never contains empty
159 DictionaryPtr
MergeDictionaries(
160 const base::DictionaryValue
* user_policy
,
161 const base::DictionaryValue
* device_policy
,
162 const base::DictionaryValue
* user_settings
,
163 const base::DictionaryValue
* shared_settings
,
164 const base::DictionaryValue
* active_settings
) {
165 hasUserPolicy_
= (user_policy
!= NULL
);
166 hasDevicePolicy_
= (device_policy
!= NULL
);
168 DictionaryPtr user_editable
;
169 if (user_policy
!= NULL
)
170 user_editable
= GetEditableFlags(*user_policy
);
172 DictionaryPtr device_editable
;
173 if (device_policy
!= NULL
)
174 device_editable
= GetEditableFlags(*device_policy
);
176 std::vector
<const base::DictionaryValue
*> dicts(kLastIndex
, NULL
);
177 dicts
[kUserPolicyIndex
] = user_policy
;
178 dicts
[kDevicePolicyIndex
] = device_policy
;
179 dicts
[kUserSettingsIndex
] = user_settings
;
180 dicts
[kSharedSettingsIndex
] = shared_settings
;
181 dicts
[kActiveSettingsIndex
] = active_settings
;
182 dicts
[kUserEditableIndex
] = user_editable
.get();
183 dicts
[kDeviceEditableIndex
] = device_editable
.get();
184 return MergeListOfDictionaries::MergeDictionaries(dicts
);
188 // This function is called by MergeDictionaries for each list of values that
189 // are located at the same path in each of the dictionaries. Implementations
190 // can use the Has*Policy functions.
191 virtual scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
192 const ValueParams
& values
) = 0;
194 // Whether a user policy was provided.
195 bool HasUserPolicy() {
196 return hasUserPolicy_
;
199 // Whether a device policy was provided.
200 bool HasDevicePolicy() {
201 return hasDevicePolicy_
;
204 // MergeListOfDictionaries override.
205 virtual scoped_ptr
<base::Value
> MergeListOfValues(
206 const std::string
& key
,
207 const std::vector
<const base::Value
*>& values
) OVERRIDE
{
208 bool user_editable
= !HasUserPolicy();
209 if (values
[kUserEditableIndex
])
210 values
[kUserEditableIndex
]->GetAsBoolean(&user_editable
);
212 bool device_editable
= !HasDevicePolicy();
213 if (values
[kDeviceEditableIndex
])
214 values
[kDeviceEditableIndex
]->GetAsBoolean(&device_editable
);
217 params
.user_policy
= values
[kUserPolicyIndex
];
218 params
.device_policy
= values
[kDevicePolicyIndex
];
219 params
.user_setting
= values
[kUserSettingsIndex
];
220 params
.shared_setting
= values
[kSharedSettingsIndex
];
221 params
.active_setting
= values
[kActiveSettingsIndex
];
222 params
.user_editable
= user_editable
;
223 params
.device_editable
= device_editable
;
224 return MergeValues(key
, params
);
232 kSharedSettingsIndex
,
233 kActiveSettingsIndex
,
235 kDeviceEditableIndex
,
239 bool hasUserPolicy_
, hasDevicePolicy_
;
241 DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies
);
244 // Call MergeDictionaries to merge policies and settings to the effective
245 // values. This ignores the active settings of Shill. See the description of
246 // MergeSettingsAndPoliciesToEffective.
247 class MergeToEffective
: public MergeSettingsAndPolicies
{
249 MergeToEffective() {}
252 // Merges |values| to the effective value (Mandatory policy overwrites user
253 // settings overwrites shared settings overwrites recommended policy). |which|
254 // is set to the respective onc::kAugmentation* constant that indicates which
255 // source of settings is effective. Note that this function may return a NULL
256 // pointer and set |which| to kAugmentationUserPolicy, which means that the
257 // user policy didn't set a value but also didn't recommend it, thus enforcing
259 scoped_ptr
<base::Value
> MergeValues(const std::string
& key
,
260 const ValueParams
& values
,
261 std::string
* which
) {
262 const base::Value
* result
= NULL
;
264 if (!values
.user_editable
) {
265 result
= values
.user_policy
;
266 *which
= kAugmentationUserPolicy
;
267 } else if (!values
.device_editable
) {
268 result
= values
.device_policy
;
269 *which
= kAugmentationDevicePolicy
;
270 } else if (values
.user_setting
) {
271 result
= values
.user_setting
;
272 *which
= kAugmentationUserSetting
;
273 } else if (values
.shared_setting
) {
274 result
= values
.shared_setting
;
275 *which
= kAugmentationSharedSetting
;
276 } else if (values
.user_policy
) {
277 result
= values
.user_policy
;
278 *which
= kAugmentationUserPolicy
;
279 } else if (values
.device_policy
) {
280 result
= values
.device_policy
;
281 *which
= kAugmentationDevicePolicy
;
283 // Can be reached if the current field is recommended, but none of the
284 // dictionaries contained a value for it.
287 return make_scoped_ptr(result
->DeepCopy());
288 return scoped_ptr
<base::Value
>();
291 // MergeSettingsAndPolicies override.
292 virtual scoped_ptr
<base::Value
> MergeValues(
293 const std::string
& key
,
294 const ValueParams
& values
) OVERRIDE
{
296 return MergeValues(key
, values
, &which
);
300 DISALLOW_COPY_AND_ASSIGN(MergeToEffective
);
303 // Call MergeDictionaries to merge policies and settings to an augmented
304 // dictionary which contains a dictionary for each value in the original
305 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
306 class MergeToAugmented
: public MergeToEffective
{
308 MergeToAugmented() {}
310 DictionaryPtr
MergeDictionaries(
311 const OncValueSignature
& signature
,
312 const base::DictionaryValue
* user_policy
,
313 const base::DictionaryValue
* device_policy
,
314 const base::DictionaryValue
* user_settings
,
315 const base::DictionaryValue
* shared_settings
,
316 const base::DictionaryValue
* active_settings
) {
317 signature_
= &signature
;
318 return MergeToEffective::MergeDictionaries(user_policy
,
326 // MergeSettingsAndPolicies override.
327 virtual scoped_ptr
<base::Value
> MergeValues(
328 const std::string
& key
,
329 const ValueParams
& values
) OVERRIDE
{
330 scoped_ptr
<base::DictionaryValue
> result(new base::DictionaryValue
);
331 if (values
.active_setting
) {
332 result
->SetWithoutPathExpansion(kAugmentationActiveSetting
,
333 values
.active_setting
->DeepCopy());
336 const OncFieldSignature
* field
= NULL
;
338 field
= GetFieldSignature(*signature_
, key
);
341 // This field is part of the provided ONCSignature, thus it can be
342 // controlled by policy.
343 std::string which_effective
;
344 MergeToEffective::MergeValues(key
, values
, &which_effective
).reset();
345 if (!which_effective
.empty()) {
346 result
->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting
,
349 bool is_credential
= onc::FieldIsCredential(*signature_
, key
);
351 // Prevent credentials from being forwarded in cleartext to
352 // UI. User/shared credentials are not stored separately, so they cannot
354 if (!is_credential
) {
355 if (values
.user_policy
) {
356 result
->SetWithoutPathExpansion(kAugmentationUserPolicy
,
357 values
.user_policy
->DeepCopy());
359 if (values
.device_policy
) {
360 result
->SetWithoutPathExpansion(kAugmentationDevicePolicy
,
361 values
.device_policy
->DeepCopy());
364 if (values
.user_setting
) {
365 result
->SetWithoutPathExpansion(kAugmentationUserSetting
,
366 values
.user_setting
->DeepCopy());
368 if (values
.shared_setting
) {
369 result
->SetWithoutPathExpansion(kAugmentationSharedSetting
,
370 values
.shared_setting
->DeepCopy());
372 if (HasUserPolicy() && values
.user_editable
) {
373 result
->SetBooleanWithoutPathExpansion(kAugmentationUserEditable
,
376 if (HasDevicePolicy() && values
.device_editable
) {
377 result
->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable
,
381 // This field is not part of the provided ONCSignature, thus it cannot be
382 // controlled by policy.
383 result
->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting
,
384 kAugmentationUnmanaged
);
388 return result
.PassAs
<base::Value
>();
391 // MergeListOfDictionaries override.
392 virtual DictionaryPtr
MergeNestedDictionaries(
393 const std::string
& key
,
394 const DictPtrs
&dicts
) OVERRIDE
{
395 DictionaryPtr result
;
397 const OncValueSignature
* enclosing_signature
= signature_
;
400 const OncFieldSignature
* field
=
401 GetFieldSignature(*enclosing_signature
, key
);
403 signature_
= field
->value_signature
;
404 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
406 signature_
= enclosing_signature
;
408 result
= MergeToEffective::MergeNestedDictionaries(key
, dicts
);
410 return result
.Pass();
414 const OncValueSignature
* signature_
;
415 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented
);
420 DictionaryPtr
MergeSettingsAndPoliciesToEffective(
421 const base::DictionaryValue
* user_policy
,
422 const base::DictionaryValue
* device_policy
,
423 const base::DictionaryValue
* user_settings
,
424 const base::DictionaryValue
* shared_settings
) {
425 MergeToEffective merger
;
426 return merger
.MergeDictionaries(
427 user_policy
, device_policy
, user_settings
, shared_settings
, NULL
);
430 DictionaryPtr
MergeSettingsAndPoliciesToAugmented(
431 const OncValueSignature
& signature
,
432 const base::DictionaryValue
* user_policy
,
433 const base::DictionaryValue
* device_policy
,
434 const base::DictionaryValue
* user_settings
,
435 const base::DictionaryValue
* shared_settings
,
436 const base::DictionaryValue
* active_settings
) {
437 MergeToAugmented merger
;
438 return merger
.MergeDictionaries(
439 signature
, user_policy
, device_policy
, user_settings
, shared_settings
,
444 } // namespace chromeos