onc_merger.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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_constants.h"
15#include "chromeos/network/onc/onc_signature.h"
16
17namespace chromeos {
18namespace onc {
19namespace {
20
21typedef scoped_ptr<base::DictionaryValue> DictionaryPtr;
22
23// Inserts |true| at every field name in |result| that is recommended in
24// |policy|.
25void MarkRecommendedFieldnames(const base::DictionaryValue& policy,
26                               base::DictionaryValue* result) {
27  const base::ListValue* recommended_value = NULL;
28  if (!policy.GetListWithoutPathExpansion(kRecommended, &recommended_value))
29    return;
30  for (base::ListValue::const_iterator it = recommended_value->begin();
31       it != recommended_value->end(); ++it) {
32    std::string entry;
33    if ((*it)->GetAsString(&entry))
34      result->SetBooleanWithoutPathExpansion(entry, true);
35  }
36}
37
38// Returns a dictionary which contains |true| at each path that is editable by
39// the user. No other fields are set.
40DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) {
41  DictionaryPtr result_editable(new base::DictionaryValue);
42  MarkRecommendedFieldnames(policy, result_editable.get());
43
44  // Recurse into nested dictionaries.
45  for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd();
46       it.Advance()) {
47    const base::DictionaryValue* child_policy = NULL;
48    if (it.key() == kRecommended ||
49        !it.value().GetAsDictionary(&child_policy)) {
50      continue;
51    }
52
53    result_editable->SetWithoutPathExpansion(
54        it.key(), GetEditableFlags(*child_policy).release());
55  }
56  return result_editable.Pass();
57}
58
59// This is the base class for merging a list of DictionaryValues in
60// parallel. See MergeDictionaries function.
61class MergeListOfDictionaries {
62 public:
63  typedef std::vector<const base::DictionaryValue*> DictPtrs;
64
65  MergeListOfDictionaries() {
66  }
67
68  virtual ~MergeListOfDictionaries() {
69  }
70
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) {
81      if (!*it_outer)
82        continue;
83
84      for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd();
85           field.Advance()) {
86        const std::string& key = field.key();
87        if (key == kRecommended || !visited.insert(key).second)
88          continue;
89
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;
96            if (*it_inner)
97              (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict);
98            nested_dicts.push_back(nested_dict);
99          }
100          DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts));
101          if (!merged_dict->empty())
102            merged_value = merged_dict.Pass();
103        } else {
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;
108            if (*it_inner)
109              (*it_inner)->GetWithoutPathExpansion(key, &value);
110            values.push_back(value);
111          }
112          merged_value = MergeListOfValues(key, values);
113        }
114
115        if (merged_value)
116          result->SetWithoutPathExpansion(key, merged_value.release());
117      }
118    }
119    return result.Pass();
120  }
121
122 protected:
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;
130
131  virtual DictionaryPtr MergeNestedDictionaries(const std::string& key,
132                                                const DictPtrs &dicts) {
133    return MergeDictionaries(dicts);
134  }
135
136 private:
137  DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries);
138};
139
140// This is the base class for merging policies and user settings.
141class MergeSettingsAndPolicies : public MergeListOfDictionaries {
142 public:
143  struct ValueParams {
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;
149    bool user_editable;
150    bool device_editable;
151  };
152
153  MergeSettingsAndPolicies() {}
154
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
158  // dictionaries.
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);
167
168    DictionaryPtr user_editable;
169    if (user_policy != NULL)
170      user_editable = GetEditableFlags(*user_policy);
171
172    DictionaryPtr device_editable;
173    if (device_policy != NULL)
174      device_editable = GetEditableFlags(*device_policy);
175
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);
185  }
186
187 protected:
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;
193
194  // Whether a user policy was provided.
195  bool HasUserPolicy() {
196    return hasUserPolicy_;
197  }
198
199  // Whether a device policy was provided.
200  bool HasDevicePolicy() {
201    return hasDevicePolicy_;
202  }
203
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);
211
212    bool device_editable = !HasDevicePolicy();
213    if (values[kDeviceEditableIndex])
214      values[kDeviceEditableIndex]->GetAsBoolean(&device_editable);
215
216    ValueParams params;
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);
225  }
226
227 private:
228  enum {
229    kUserPolicyIndex,
230    kDevicePolicyIndex,
231    kUserSettingsIndex,
232    kSharedSettingsIndex,
233    kActiveSettingsIndex,
234    kUserEditableIndex,
235    kDeviceEditableIndex,
236    kLastIndex
237  };
238
239  bool hasUserPolicy_, hasDevicePolicy_;
240
241  DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies);
242};
243
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.
247class MergeToEffective : public MergeSettingsAndPolicies {
248 public:
249  MergeToEffective() {}
250
251 protected:
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
258  // the empty value.
259  scoped_ptr<base::Value> MergeValues(const std::string& key,
260                                      const ValueParams& values,
261                                      std::string* which) {
262    const base::Value* result = NULL;
263    which->clear();
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;
282    } else {
283      // Can be reached if the current field is recommended, but none of the
284      // dictionaries contained a value for it.
285    }
286    if (result)
287      return make_scoped_ptr(result->DeepCopy());
288    return scoped_ptr<base::Value>();
289  }
290
291  // MergeSettingsAndPolicies override.
292  virtual scoped_ptr<base::Value> MergeValues(
293      const std::string& key,
294      const ValueParams& values) OVERRIDE {
295    std::string which;
296    return MergeValues(key, values, &which);
297  }
298
299 private:
300  DISALLOW_COPY_AND_ASSIGN(MergeToEffective);
301};
302
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.
306class MergeToAugmented : public MergeToEffective {
307 public:
308  MergeToAugmented() {}
309
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,
319                                               device_policy,
320                                               user_settings,
321                                               shared_settings,
322                                               active_settings);
323  }
324
325 protected:
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());
334    }
335
336    const OncFieldSignature* field = NULL;
337    if (signature_)
338      field = GetFieldSignature(*signature_, key);
339
340    if (field) {
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,
347                                              which_effective);
348      }
349      bool is_credential = onc::FieldIsCredential(*signature_, key);
350
351      // Prevent credentials from being forwarded in cleartext to
352      // UI. User/shared credentials are not stored separately, so they cannot
353      // leak here.
354      if (!is_credential) {
355        if (values.user_policy) {
356          result->SetWithoutPathExpansion(kAugmentationUserPolicy,
357                                          values.user_policy->DeepCopy());
358        }
359        if (values.device_policy) {
360          result->SetWithoutPathExpansion(kAugmentationDevicePolicy,
361                                          values.device_policy->DeepCopy());
362        }
363      }
364      if (values.user_setting) {
365        result->SetWithoutPathExpansion(kAugmentationUserSetting,
366                                        values.user_setting->DeepCopy());
367      }
368      if (values.shared_setting) {
369        result->SetWithoutPathExpansion(kAugmentationSharedSetting,
370                                        values.shared_setting->DeepCopy());
371      }
372      if (HasUserPolicy() && values.user_editable) {
373        result->SetBooleanWithoutPathExpansion(kAugmentationUserEditable,
374                                               true);
375      }
376      if (HasDevicePolicy() && values.device_editable) {
377        result->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable,
378                                               true);
379      }
380    } else {
381      // This field is not part of the provided ONCSignature, thus it cannot be
382      // controlled by policy.
383      result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting,
384                                            kAugmentationUnmanaged);
385    }
386    if (result->empty())
387      result.reset();
388    return result.PassAs<base::Value>();
389  }
390
391  // MergeListOfDictionaries override.
392  virtual DictionaryPtr MergeNestedDictionaries(
393      const std::string& key,
394      const DictPtrs &dicts) OVERRIDE {
395    DictionaryPtr result;
396    if (signature_) {
397      const OncValueSignature* enclosing_signature = signature_;
398      signature_ = NULL;
399
400      const OncFieldSignature* field =
401          GetFieldSignature(*enclosing_signature, key);
402      if (field)
403        signature_ = field->value_signature;
404      result = MergeToEffective::MergeNestedDictionaries(key, dicts);
405
406      signature_ = enclosing_signature;
407    } else {
408      result = MergeToEffective::MergeNestedDictionaries(key, dicts);
409    }
410    return result.Pass();
411  }
412
413 private:
414  const OncValueSignature* signature_;
415  DISALLOW_COPY_AND_ASSIGN(MergeToAugmented);
416};
417
418}  // namespace
419
420DictionaryPtr 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);
428}
429
430DictionaryPtr 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,
440      active_settings);
441}
442
443}  // namespace onc
444}  // namespace chromeos
445