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.
4
5 #include "chromeos/network/onc/onc_merger.h"
6
7 #include <set>
8 #include <string>
9 #include <vector>
10
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"
16
17 namespace chromeos {
18 namespace onc {
19 namespace {
20
21 typedef scoped_ptr<base::DictionaryValue> DictionaryPtr;
22
23 // Inserts |true| at every field name in |result| that is recommended in
24 // |policy|.
MarkRecommendedFieldnames(const base::DictionaryValue & policy,base::DictionaryValue * result)25 void MarkRecommendedFieldnames(const base::DictionaryValue& policy,
26 base::DictionaryValue* result) {
27 const base::ListValue* recommended_value = NULL;
28 if (!policy.GetListWithoutPathExpansion(::onc::kRecommended,
29 &recommended_value))
30 return;
31 for (base::ListValue::const_iterator it = recommended_value->begin();
32 it != recommended_value->end(); ++it) {
33 std::string entry;
34 if ((*it)->GetAsString(&entry))
35 result->SetBooleanWithoutPathExpansion(entry, true);
36 }
37 }
38
39 // Returns a dictionary which contains |true| at each path that is editable by
40 // the user. No other fields are set.
GetEditableFlags(const base::DictionaryValue & policy)41 DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) {
42 DictionaryPtr result_editable(new base::DictionaryValue);
43 MarkRecommendedFieldnames(policy, result_editable.get());
44
45 // Recurse into nested dictionaries.
46 for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd();
47 it.Advance()) {
48 const base::DictionaryValue* child_policy = NULL;
49 if (it.key() == ::onc::kRecommended ||
50 !it.value().GetAsDictionary(&child_policy)) {
51 continue;
52 }
53
54 result_editable->SetWithoutPathExpansion(
55 it.key(), GetEditableFlags(*child_policy).release());
56 }
57 return result_editable.Pass();
58 }
59
60 // This is the base class for merging a list of DictionaryValues in
61 // parallel. See MergeDictionaries function.
62 class MergeListOfDictionaries {
63 public:
64 typedef std::vector<const base::DictionaryValue*> DictPtrs;
65
MergeListOfDictionaries()66 MergeListOfDictionaries() {
67 }
68
~MergeListOfDictionaries()69 virtual ~MergeListOfDictionaries() {
70 }
71
72 // For each path in any of the dictionaries |dicts|, the function
73 // MergeListOfValues is called with the list of values that are located at
74 // that path in each of the dictionaries. This function returns a new
75 // dictionary containing all results of MergeListOfValues at the respective
76 // paths. The resulting dictionary doesn't contain empty dictionaries.
MergeDictionaries(const DictPtrs & dicts)77 DictionaryPtr MergeDictionaries(const DictPtrs &dicts) {
78 DictionaryPtr result(new base::DictionaryValue);
79 std::set<std::string> visited;
80 for (DictPtrs::const_iterator it_outer = dicts.begin();
81 it_outer != dicts.end(); ++it_outer) {
82 if (!*it_outer)
83 continue;
84
85 for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd();
86 field.Advance()) {
87 const std::string& key = field.key();
88 if (key == ::onc::kRecommended || !visited.insert(key).second)
89 continue;
90
91 scoped_ptr<base::Value> merged_value;
92 if (field.value().IsType(base::Value::TYPE_DICTIONARY)) {
93 DictPtrs nested_dicts;
94 for (DictPtrs::const_iterator it_inner = dicts.begin();
95 it_inner != dicts.end(); ++it_inner) {
96 const base::DictionaryValue* nested_dict = NULL;
97 if (*it_inner)
98 (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict);
99 nested_dicts.push_back(nested_dict);
100 }
101 DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts));
102 if (!merged_dict->empty())
103 merged_value = merged_dict.Pass();
104 } else {
105 std::vector<const base::Value*> values;
106 for (DictPtrs::const_iterator it_inner = dicts.begin();
107 it_inner != dicts.end(); ++it_inner) {
108 const base::Value* value = NULL;
109 if (*it_inner)
110 (*it_inner)->GetWithoutPathExpansion(key, &value);
111 values.push_back(value);
112 }
113 merged_value = MergeListOfValues(key, values);
114 }
115
116 if (merged_value)
117 result->SetWithoutPathExpansion(key, merged_value.release());
118 }
119 }
120 return result.Pass();
121 }
122
123 protected:
124 // This function is called by MergeDictionaries for each list of values that
125 // are located at the same path in each of the dictionaries. The order of the
126 // values is the same as of the given dictionaries |dicts|. If a dictionary
127 // doesn't contain a path then it's value is NULL.
128 virtual scoped_ptr<base::Value> MergeListOfValues(
129 const std::string& key,
130 const std::vector<const base::Value*>& values) = 0;
131
MergeNestedDictionaries(const std::string & key,const DictPtrs & dicts)132 virtual DictionaryPtr MergeNestedDictionaries(const std::string& key,
133 const DictPtrs &dicts) {
134 return MergeDictionaries(dicts);
135 }
136
137 private:
138 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries);
139 };
140
141 // This is the base class for merging policies and user settings.
142 class MergeSettingsAndPolicies : public MergeListOfDictionaries {
143 public:
144 struct ValueParams {
145 const base::Value* user_policy;
146 const base::Value* device_policy;
147 const base::Value* user_setting;
148 const base::Value* shared_setting;
149 const base::Value* active_setting;
150 bool user_editable;
151 bool device_editable;
152 };
153
MergeSettingsAndPolicies()154 MergeSettingsAndPolicies() {}
155
156 // Merge the provided dictionaries. For each path in any of the dictionaries,
157 // MergeValues is called. Its results are collected in a new dictionary which
158 // is then returned. The resulting dictionary never contains empty
159 // dictionaries.
MergeDictionaries(const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings,const base::DictionaryValue * active_settings)160 DictionaryPtr MergeDictionaries(
161 const base::DictionaryValue* user_policy,
162 const base::DictionaryValue* device_policy,
163 const base::DictionaryValue* user_settings,
164 const base::DictionaryValue* shared_settings,
165 const base::DictionaryValue* active_settings) {
166 hasUserPolicy_ = (user_policy != NULL);
167 hasDevicePolicy_ = (device_policy != NULL);
168
169 DictionaryPtr user_editable;
170 if (user_policy != NULL)
171 user_editable = GetEditableFlags(*user_policy);
172
173 DictionaryPtr device_editable;
174 if (device_policy != NULL)
175 device_editable = GetEditableFlags(*device_policy);
176
177 std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL);
178 dicts[kUserPolicyIndex] = user_policy;
179 dicts[kDevicePolicyIndex] = device_policy;
180 dicts[kUserSettingsIndex] = user_settings;
181 dicts[kSharedSettingsIndex] = shared_settings;
182 dicts[kActiveSettingsIndex] = active_settings;
183 dicts[kUserEditableIndex] = user_editable.get();
184 dicts[kDeviceEditableIndex] = device_editable.get();
185 return MergeListOfDictionaries::MergeDictionaries(dicts);
186 }
187
188 protected:
189 // This function is called by MergeDictionaries for each list of values that
190 // are located at the same path in each of the dictionaries. Implementations
191 // can use the Has*Policy functions.
192 virtual scoped_ptr<base::Value> MergeValues(const std::string& key,
193 const ValueParams& values) = 0;
194
195 // Whether a user policy was provided.
HasUserPolicy()196 bool HasUserPolicy() {
197 return hasUserPolicy_;
198 }
199
200 // Whether a device policy was provided.
HasDevicePolicy()201 bool HasDevicePolicy() {
202 return hasDevicePolicy_;
203 }
204
205 // MergeListOfDictionaries override.
MergeListOfValues(const std::string & key,const std::vector<const base::Value * > & values)206 virtual scoped_ptr<base::Value> MergeListOfValues(
207 const std::string& key,
208 const std::vector<const base::Value*>& values) OVERRIDE {
209 bool user_editable = !HasUserPolicy();
210 if (values[kUserEditableIndex])
211 values[kUserEditableIndex]->GetAsBoolean(&user_editable);
212
213 bool device_editable = !HasDevicePolicy();
214 if (values[kDeviceEditableIndex])
215 values[kDeviceEditableIndex]->GetAsBoolean(&device_editable);
216
217 ValueParams params;
218 params.user_policy = values[kUserPolicyIndex];
219 params.device_policy = values[kDevicePolicyIndex];
220 params.user_setting = values[kUserSettingsIndex];
221 params.shared_setting = values[kSharedSettingsIndex];
222 params.active_setting = values[kActiveSettingsIndex];
223 params.user_editable = user_editable;
224 params.device_editable = device_editable;
225 return MergeValues(key, params);
226 }
227
228 private:
229 enum {
230 kUserPolicyIndex,
231 kDevicePolicyIndex,
232 kUserSettingsIndex,
233 kSharedSettingsIndex,
234 kActiveSettingsIndex,
235 kUserEditableIndex,
236 kDeviceEditableIndex,
237 kLastIndex
238 };
239
240 bool hasUserPolicy_, hasDevicePolicy_;
241
242 DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies);
243 };
244
245 // Call MergeDictionaries to merge policies and settings to the effective
246 // values. This ignores the active settings of Shill. See the description of
247 // MergeSettingsAndPoliciesToEffective.
248 class MergeToEffective : public MergeSettingsAndPolicies {
249 public:
MergeToEffective()250 MergeToEffective() {}
251
252 protected:
253 // Merges |values| to the effective value (Mandatory policy overwrites user
254 // settings overwrites shared settings overwrites recommended policy). |which|
255 // is set to the respective onc::kAugmentation* constant that indicates which
256 // source of settings is effective. Note that this function may return a NULL
257 // pointer and set |which| to ::onc::kAugmentationUserPolicy, which means that
258 // the
259 // user policy didn't set a value but also didn't recommend it, thus enforcing
260 // the empty value.
MergeValues(const std::string & key,const ValueParams & values,std::string * which)261 scoped_ptr<base::Value> MergeValues(const std::string& key,
262 const ValueParams& values,
263 std::string* which) {
264 const base::Value* result = NULL;
265 which->clear();
266 if (!values.user_editable) {
267 result = values.user_policy;
268 *which = ::onc::kAugmentationUserPolicy;
269 } else if (!values.device_editable) {
270 result = values.device_policy;
271 *which = ::onc::kAugmentationDevicePolicy;
272 } else if (values.user_setting) {
273 result = values.user_setting;
274 *which = ::onc::kAugmentationUserSetting;
275 } else if (values.shared_setting) {
276 result = values.shared_setting;
277 *which = ::onc::kAugmentationSharedSetting;
278 } else if (values.user_policy) {
279 result = values.user_policy;
280 *which = ::onc::kAugmentationUserPolicy;
281 } else if (values.device_policy) {
282 result = values.device_policy;
283 *which = ::onc::kAugmentationDevicePolicy;
284 } else {
285 // Can be reached if the current field is recommended, but none of the
286 // dictionaries contained a value for it.
287 }
288 if (result)
289 return make_scoped_ptr(result->DeepCopy());
290 return scoped_ptr<base::Value>();
291 }
292
293 // MergeSettingsAndPolicies override.
MergeValues(const std::string & key,const ValueParams & values)294 virtual scoped_ptr<base::Value> MergeValues(
295 const std::string& key,
296 const ValueParams& values) OVERRIDE {
297 std::string which;
298 return MergeValues(key, values, &which);
299 }
300
301 private:
302 DISALLOW_COPY_AND_ASSIGN(MergeToEffective);
303 };
304
305 // Call MergeDictionaries to merge policies and settings to an augmented
306 // dictionary which contains a dictionary for each value in the original
307 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
308 class MergeToAugmented : public MergeToEffective {
309 public:
MergeToAugmented()310 MergeToAugmented() {}
311
MergeDictionaries(const OncValueSignature & signature,const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings,const base::DictionaryValue * active_settings)312 DictionaryPtr MergeDictionaries(
313 const OncValueSignature& signature,
314 const base::DictionaryValue* user_policy,
315 const base::DictionaryValue* device_policy,
316 const base::DictionaryValue* user_settings,
317 const base::DictionaryValue* shared_settings,
318 const base::DictionaryValue* active_settings) {
319 signature_ = &signature;
320 return MergeToEffective::MergeDictionaries(user_policy,
321 device_policy,
322 user_settings,
323 shared_settings,
324 active_settings);
325 }
326
327 protected:
328 // MergeSettingsAndPolicies override.
MergeValues(const std::string & key,const ValueParams & values)329 virtual scoped_ptr<base::Value> MergeValues(
330 const std::string& key,
331 const ValueParams& values) OVERRIDE {
332 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
333 if (values.active_setting) {
334 result->SetWithoutPathExpansion(::onc::kAugmentationActiveSetting,
335 values.active_setting->DeepCopy());
336 }
337
338 const OncFieldSignature* field = NULL;
339 if (signature_)
340 field = GetFieldSignature(*signature_, key);
341
342 if (field) {
343 // This field is part of the provided ONCSignature, thus it can be
344 // controlled by policy.
345 std::string which_effective;
346 MergeToEffective::MergeValues(key, values, &which_effective).reset();
347 if (!which_effective.empty()) {
348 result->SetStringWithoutPathExpansion(
349 ::onc::kAugmentationEffectiveSetting, which_effective);
350 }
351 bool is_credential = onc::FieldIsCredential(*signature_, key);
352
353 // Prevent credentials from being forwarded in cleartext to
354 // UI. User/shared credentials are not stored separately, so they cannot
355 // leak here.
356 if (!is_credential) {
357 if (values.user_policy) {
358 result->SetWithoutPathExpansion(::onc::kAugmentationUserPolicy,
359 values.user_policy->DeepCopy());
360 }
361 if (values.device_policy) {
362 result->SetWithoutPathExpansion(::onc::kAugmentationDevicePolicy,
363 values.device_policy->DeepCopy());
364 }
365 }
366 if (values.user_setting) {
367 result->SetWithoutPathExpansion(::onc::kAugmentationUserSetting,
368 values.user_setting->DeepCopy());
369 }
370 if (values.shared_setting) {
371 result->SetWithoutPathExpansion(::onc::kAugmentationSharedSetting,
372 values.shared_setting->DeepCopy());
373 }
374 if (HasUserPolicy() && values.user_editable) {
375 result->SetBooleanWithoutPathExpansion(::onc::kAugmentationUserEditable,
376 true);
377 }
378 if (HasDevicePolicy() && values.device_editable) {
379 result->SetBooleanWithoutPathExpansion(
380 ::onc::kAugmentationDeviceEditable, true);
381 }
382 } else {
383 // This field is not part of the provided ONCSignature, thus it cannot be
384 // controlled by policy.
385 result->SetStringWithoutPathExpansion(
386 ::onc::kAugmentationEffectiveSetting, ::onc::kAugmentationUnmanaged);
387 }
388 if (result->empty())
389 result.reset();
390 return result.PassAs<base::Value>();
391 }
392
393 // MergeListOfDictionaries override.
MergeNestedDictionaries(const std::string & key,const DictPtrs & dicts)394 virtual DictionaryPtr MergeNestedDictionaries(
395 const std::string& key,
396 const DictPtrs &dicts) OVERRIDE {
397 DictionaryPtr result;
398 if (signature_) {
399 const OncValueSignature* enclosing_signature = signature_;
400 signature_ = NULL;
401
402 const OncFieldSignature* field =
403 GetFieldSignature(*enclosing_signature, key);
404 if (field)
405 signature_ = field->value_signature;
406 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
407
408 signature_ = enclosing_signature;
409 } else {
410 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
411 }
412 return result.Pass();
413 }
414
415 private:
416 const OncValueSignature* signature_;
417 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented);
418 };
419
420 } // namespace
421
MergeSettingsAndPoliciesToEffective(const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings)422 DictionaryPtr MergeSettingsAndPoliciesToEffective(
423 const base::DictionaryValue* user_policy,
424 const base::DictionaryValue* device_policy,
425 const base::DictionaryValue* user_settings,
426 const base::DictionaryValue* shared_settings) {
427 MergeToEffective merger;
428 return merger.MergeDictionaries(
429 user_policy, device_policy, user_settings, shared_settings, NULL);
430 }
431
MergeSettingsAndPoliciesToAugmented(const OncValueSignature & signature,const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings,const base::DictionaryValue * active_settings)432 DictionaryPtr MergeSettingsAndPoliciesToAugmented(
433 const OncValueSignature& signature,
434 const base::DictionaryValue* user_policy,
435 const base::DictionaryValue* device_policy,
436 const base::DictionaryValue* user_settings,
437 const base::DictionaryValue* shared_settings,
438 const base::DictionaryValue* active_settings) {
439 MergeToAugmented merger;
440 return merger.MergeDictionaries(
441 signature, user_policy, device_policy, user_settings, shared_settings,
442 active_settings);
443 }
444
445 } // namespace onc
446 } // namespace chromeos
447