onc_merger.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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
16namespace chromeos {
17namespace onc {
18namespace {
19
20typedef scoped_ptr<base::DictionaryValue> DictionaryPtr;
21
22// Inserts |true| at every field name in |result| that is recommended in
23// |policy|.
24void MarkRecommendedFieldnames(const base::DictionaryValue& policy,
25                               base::DictionaryValue* result) {
26  const ListValue* recommended_value = NULL;
27  if (!policy.GetListWithoutPathExpansion(kRecommended, &recommended_value))
28    return;
29  for (ListValue::const_iterator it = recommended_value->begin();
30       it != recommended_value->end(); ++it) {
31    std::string entry;
32    if ((*it)->GetAsString(&entry))
33      result->SetBooleanWithoutPathExpansion(entry, true);
34  }
35}
36
37// Returns a dictionary which contains |true| at each path that is editable by
38// the user. No other fields are set.
39DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) {
40  DictionaryPtr result_editable(new base::DictionaryValue);
41  MarkRecommendedFieldnames(policy, result_editable.get());
42
43  // Recurse into nested dictionaries.
44  for (base::DictionaryValue::Iterator it(policy); it.HasNext(); it.Advance()) {
45    const base::DictionaryValue* child_policy = NULL;
46    if (it.key() == kRecommended ||
47        !it.value().GetAsDictionary(&child_policy)) {
48      continue;
49    }
50
51    result_editable->SetWithoutPathExpansion(
52        it.key(), GetEditableFlags(*child_policy).release());
53  }
54  return result_editable.Pass();
55}
56
57// This is the base class for merging a list of DictionaryValues in
58// parallel. See MergeDictionaries function.
59class MergeListOfDictionaries {
60 public:
61  typedef std::vector<const base::DictionaryValue*> DictPtrs;
62
63  MergeListOfDictionaries() {
64  }
65
66  virtual ~MergeListOfDictionaries() {
67  }
68
69  // For each path in any of the dictionaries |dicts|, the function
70  // MergeListOfValues is called with the list of values that are located at
71  // that path in each of the dictionaries. This function returns a new
72  // dictionary containing all results of MergeListOfValues at the respective
73  // paths. The resulting dictionary doesn't contain empty dictionaries.
74  DictionaryPtr MergeDictionaries(const DictPtrs &dicts) {
75    DictionaryPtr result(new base::DictionaryValue);
76    std::set<std::string> visited;
77    for (DictPtrs::const_iterator it_outer = dicts.begin();
78         it_outer != dicts.end(); ++it_outer) {
79      if (!*it_outer)
80        continue;
81
82      for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd();
83           field.Advance()) {
84        const std::string& key = field.key();
85        if (key == kRecommended || !visited.insert(key).second)
86          continue;
87
88        scoped_ptr<base::Value> merged_value;
89        if (field.value().IsType(base::Value::TYPE_DICTIONARY)) {
90          DictPtrs nested_dicts;
91          for (DictPtrs::const_iterator it_inner = dicts.begin();
92               it_inner != dicts.end(); ++it_inner) {
93            const base::DictionaryValue* nested_dict = NULL;
94            if (*it_inner)
95              (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict);
96            nested_dicts.push_back(nested_dict);
97          }
98          DictionaryPtr merged_dict(MergeDictionaries(nested_dicts));
99          if (!merged_dict->empty())
100            merged_value = merged_dict.Pass();
101        } else {
102          std::vector<const base::Value*> values;
103          for (DictPtrs::const_iterator it_inner = dicts.begin();
104               it_inner != dicts.end(); ++it_inner) {
105            const base::Value* value = NULL;
106            if (*it_inner)
107              (*it_inner)->GetWithoutPathExpansion(key, &value);
108            values.push_back(value);
109          }
110          merged_value = MergeListOfValues(values);
111        }
112
113        if (merged_value)
114          result->SetWithoutPathExpansion(key, merged_value.release());
115      }
116    }
117    return result.Pass();
118  }
119
120 protected:
121  // This function is called by MergeDictionaries for each list of values that
122  // are located at the same path in each of the dictionaries. The order of the
123  // values is the same as of the given dictionaries |dicts|. If a dictionary
124  // doesn't contain a path then it's value is NULL.
125  virtual scoped_ptr<base::Value> MergeListOfValues(
126      const std::vector<const base::Value*>& values) = 0;
127
128 private:
129  DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries);
130};
131
132// This is the base class for merging policies and user settings.
133class MergeSettingsAndPolicies : public MergeListOfDictionaries {
134 public:
135  struct ValueParams {
136    const base::Value* user_policy;
137    const base::Value* device_policy;
138    const base::Value* user_settings;
139    const base::Value* shared_settings;
140    bool user_editable;
141    bool device_editable;
142  };
143
144  // Merge the provided dictionaries. For each path in any of the dictionaries,
145  // MergeValues is called. Its results are collected in a new dictionary which
146  // is then returned. The resulting dictionary never contains empty
147  // dictionaries.
148  DictionaryPtr MergeDictionaries(
149      const base::DictionaryValue* user_policy,
150      const base::DictionaryValue* device_policy,
151      const base::DictionaryValue* user_settings,
152      const base::DictionaryValue* shared_settings) {
153    hasUserPolicy_ = (user_policy != NULL);
154    hasDevicePolicy_ = (device_policy != NULL);
155
156    DictionaryPtr user_editable;
157    if (user_policy != NULL)
158      user_editable = GetEditableFlags(*user_policy);
159
160    DictionaryPtr device_editable;
161    if (device_policy != NULL)
162      device_editable = GetEditableFlags(*device_policy);
163
164    std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL);
165    dicts[kUserPolicyIndex] = user_policy;
166    dicts[kDevicePolicyIndex] = device_policy;
167    dicts[kUserSettingsIndex] = user_settings;
168    dicts[kSharedSettingsIndex] = shared_settings;
169    dicts[kUserEditableIndex] = user_editable.get();
170    dicts[kDeviceEditableIndex] = device_editable.get();
171    return MergeListOfDictionaries::MergeDictionaries(dicts);
172  }
173
174 protected:
175  // This function is called by MergeDictionaries for each list of values that
176  // are located at the same path in each of the dictionaries. Implementations
177  // can use the Has*Policy functions.
178  virtual scoped_ptr<base::Value> MergeValues(ValueParams values) = 0;
179
180  // Whether a user policy was provided.
181  bool HasUserPolicy() {
182    return hasUserPolicy_;
183  }
184
185  // Whether a device policy was provided.
186  bool HasDevicePolicy() {
187    return hasDevicePolicy_;
188  }
189
190  // MergeListOfDictionaries override.
191  virtual scoped_ptr<base::Value> MergeListOfValues(
192      const std::vector<const base::Value*>& values) OVERRIDE {
193    bool user_editable = !HasUserPolicy();
194    if (values[kUserEditableIndex])
195      values[kUserEditableIndex]->GetAsBoolean(&user_editable);
196
197    bool device_editable = !HasDevicePolicy();
198    if (values[kDeviceEditableIndex])
199      values[kDeviceEditableIndex]->GetAsBoolean(&device_editable);
200
201    ValueParams params;
202    params.user_policy = values[kUserPolicyIndex];
203    params.device_policy = values[kDevicePolicyIndex];
204    params.user_settings = values[kUserSettingsIndex];
205    params.shared_settings = values[kSharedSettingsIndex];
206    params.user_editable = user_editable;
207    params.device_editable = device_editable;
208    return MergeValues(params);
209  }
210
211 private:
212  enum {
213    kUserPolicyIndex,
214    kDevicePolicyIndex,
215    kUserSettingsIndex,
216    kSharedSettingsIndex,
217    kUserEditableIndex,
218    kDeviceEditableIndex,
219    kLastIndex
220  };
221
222  bool hasUserPolicy_, hasDevicePolicy_;
223};
224
225// Call MergeDictionaries to merge policies and settings to the effective
226// values. See the description of MergeSettingsAndPoliciesToEffective.
227class MergeToEffective : public MergeSettingsAndPolicies {
228 protected:
229  // Merges |values| to the effective value (Mandatory policy overwrites user
230  // settings overwrites shared settings overwrites recommended policy). |which|
231  // is set to the respective onc::kAugmentation* constant that indicates which
232  // source of settings is effective. Note that this function may return a NULL
233  // pointer and set |which| to kAugmentationUserPolicy, which means that the
234  // user policy didn't set a value but also didn't recommend it, thus enforcing
235  // the empty value.
236  scoped_ptr<base::Value> MergeValues(ValueParams values, std::string* which) {
237    const base::Value* result = NULL;
238    which->clear();
239    if (!values.user_editable) {
240      result = values.user_policy;
241      *which = kAugmentationUserPolicy;
242    } else if (!values.device_editable) {
243      result = values.device_policy;
244      *which = kAugmentationDevicePolicy;
245    } else if (values.user_settings) {
246      result = values.user_settings;
247      *which = kAugmentationUserSetting;
248    } else if (values.shared_settings) {
249      result = values.shared_settings;
250      *which = kAugmentationSharedSetting;
251    } else if (values.user_policy) {
252      result = values.user_policy;
253      *which = kAugmentationUserPolicy;
254    } else if (values.device_policy) {
255      result = values.device_policy;
256      *which = kAugmentationDevicePolicy;
257    } else {
258      // Can be reached if the current field is recommended, but none of the
259      // dictionaries contained a value for it.
260    }
261    if (result)
262      return make_scoped_ptr(result->DeepCopy());
263    return scoped_ptr<base::Value>();
264  }
265
266  // MergeSettingsAndPolicies override.
267  virtual scoped_ptr<base::Value> MergeValues(
268      ValueParams values) OVERRIDE {
269    std::string which;
270    return MergeValues(values, &which);
271  }
272};
273
274// Call MergeDictionaries to merge policies and settings to an augmented
275// dictionary which contains a dictionary for each value in the original
276// dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
277class MergeToAugmented : public MergeToEffective {
278 protected:
279  // MergeSettingsAndPolicies override.
280  virtual scoped_ptr<base::Value> MergeValues(
281      ValueParams values) OVERRIDE {
282    scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
283    std::string which_effective;
284    MergeToEffective::MergeValues(values, &which_effective).reset();
285    if (!which_effective.empty()) {
286      result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting,
287                                            which_effective);
288    }
289    if (values.user_policy) {
290      result->SetWithoutPathExpansion(kAugmentationUserPolicy,
291                                      values.user_policy->DeepCopy());
292    }
293    if (values.device_policy) {
294      result->SetWithoutPathExpansion(kAugmentationDevicePolicy,
295                                      values.device_policy->DeepCopy());
296    }
297    if (values.user_settings) {
298      result->SetWithoutPathExpansion(kAugmentationUserSetting,
299                                      values.user_settings->DeepCopy());
300    }
301    if (values.shared_settings) {
302      result->SetWithoutPathExpansion(kAugmentationSharedSetting,
303                                      values.shared_settings->DeepCopy());
304    }
305    if (HasUserPolicy() && values.user_editable) {
306      result->SetBooleanWithoutPathExpansion(kAugmentationUserEditable,
307                                             values.user_editable);
308    }
309    if (HasDevicePolicy() && values.device_editable) {
310      result->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable,
311                                             values.device_editable);
312    }
313    return result.PassAs<base::Value>();
314  }
315};
316
317}  // namespace
318
319DictionaryPtr MergeSettingsAndPoliciesToEffective(
320    const base::DictionaryValue* user_policy,
321    const base::DictionaryValue* device_policy,
322    const base::DictionaryValue* user_settings,
323    const base::DictionaryValue* shared_settings) {
324  MergeToEffective merger;
325  return merger.MergeDictionaries(
326      user_policy, device_policy, user_settings, shared_settings);
327}
328
329DictionaryPtr MergeSettingsAndPoliciesToAugmented(
330    const base::DictionaryValue* user_policy,
331    const base::DictionaryValue* device_policy,
332    const base::DictionaryValue* user_settings,
333    const base::DictionaryValue* shared_settings) {
334  MergeToAugmented merger;
335  return merger.MergeDictionaries(
336      user_policy, device_policy, user_settings, shared_settings);
337}
338
339}  // namespace onc
340}  // namespace chromeos
341